Libpolycomp  1.0
A compression/decompression library that implements the polynomial compression and other simple compression schemes
quant.c
1 /* quant.c - Quantization of floating-point numbers
2  *
3  * Copyright (c) 2015 Maurizio Tomasi
4  *
5  * Permission is hereby granted, free of charge, to any person
6  * obtaining a copy of this software and associated documentation
7  * files (the "Software"), to deal in the Software without
8  * restriction, including without limitation the rights to use, copy,
9  * modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include "libpolycomp.h"
27 #include <limits.h> /* CHAR_BIT */
28 #include <math.h>
29 #include <stdlib.h>
30 
73 /***********************************************************************
74  * Creation/destruction/getter functions for "pcomp_quant_params_t"
75  */
76 
78  size_t element_size;
79  size_t bits_per_sample;
80  double min_value;
81  double normalization;
82 };
83 
105  size_t bits_per_sample)
106 {
107  pcomp_quant_params_t* params = malloc(sizeof(pcomp_quant_params_t));
108  params->element_size = element_size;
109  params->bits_per_sample = bits_per_sample;
110  params->min_value = 0.0;
111  params->normalization = 1.0;
112 
113  return params;
114 }
115 
129 {
130  if (params == NULL)
131  return;
132  free(params);
133 }
134 
143 {
144  if (params == NULL)
145  abort();
146 
147  return params->element_size;
148 }
149 
156 {
157  if (params == NULL)
158  abort();
159 
160  return params->bits_per_sample;
161 }
162 
171 {
172  if (params == NULL)
173  abort();
174 
175  return params->normalization;
176 }
177 
186 {
187  if (params == NULL)
188  abort();
189 
190  return params->min_value;
191 }
192 
213  double normalization, double offset)
214 {
215  if (params == NULL)
216  abort();
217 
218  params->normalization = normalization;
219  params->min_value = offset;
220 }
221 
222 /***********************************************************************
223  * Estimate the size of the buffer needed to store quantized data
224  */
225 
244 size_t pcomp_quant_bufsize(size_t input_size,
245  const pcomp_quant_params_t* params)
246 {
247  size_t num_of_bits;
248  size_t result;
249 
250  if (params == NULL)
251  abort();
252 
253  if (params->element_size == 0 || params->bits_per_sample == 0)
254  return 0;
255 
256  num_of_bits = input_size * params->element_size * CHAR_BIT;
257  result = num_of_bits / params->bits_per_sample;
258  if (num_of_bits % params->bits_per_sample > 0)
259  result++;
260 
261  return result;
262 }
263 
264 /***********************************************************************
265  * Quantization compression functions
266  */
267 
356 #define DEFINE_FIND_BOUNDS_FN(name, datatype_t) \
357  static void name(const datatype_t* values, size_t num, \
358  double* min, double* max) \
359  { \
360  size_t idx; \
361  \
362  if (values == NULL || num == 0 || min == NULL || max == NULL) \
363  abort(); \
364  \
365  *min = *max = values[0]; \
366  for (idx = 1; idx < num; ++idx) { \
367  if (values[idx] < *min) \
368  *min = values[idx]; \
369  else if (values[idx] > *max) \
370  *max = values[idx]; \
371  } \
372  }
373 
374 DEFINE_FIND_BOUNDS_FN(find_bounds_float, float)
375 DEFINE_FIND_BOUNDS_FN(find_bounds_double, double)
376 
377 static double two_to(int exponent)
378 {
379  double result = 2.0;
380  int idx;
381  for (idx = 2; idx <= exponent; ++idx)
382  result *= 2.0;
383 
384  return result;
385 }
386 
387 #define DEFINE_COMPRESS_QUANT_FN(name, find_bounds_fn, datatype_t) \
388  int name(void* output_buf, size_t* output_size, \
389  const datatype_t* input_buf, size_t input_size, \
390  pcomp_quant_params_t* params) \
391  { \
392  double max; \
393  uint8_t* byte_buf \
394  = output_buf; /* Casting to "uint8_t" is far easier... */ \
395  uint8_t cur_byte_buf = 0; \
396  size_t bits_in_buffer = 0; \
397  size_t idx; \
398  \
399  if (output_buf == NULL || output_size == NULL \
400  || input_buf == NULL || params == NULL) \
401  abort(); \
402  \
403  find_bounds_fn(input_buf, input_size, &params->min_value, \
404  &max); \
405  params->normalization = (two_to(params->bits_per_sample) \
406  - 1.0) / (max - params->min_value); \
407  \
408  for (idx = 0; idx < input_size; ++idx) { \
409  size_t bit_idx; \
410  double scaled_value \
411  = floor((input_buf[idx] - params->min_value) \
412  * params->normalization \
413  + 0.5); \
414  \
415  for (bit_idx = 0; bit_idx < params->bits_per_sample; \
416  ++bit_idx) { \
417  uint8_t bit = (uint8_t)floor(fmod(scaled_value, 2.0)); \
418  cur_byte_buf = (cur_byte_buf << 1) + bit; \
419  bits_in_buffer++; \
420  scaled_value /= 2.0; \
421  \
422  if (bits_in_buffer >= CHAR_BIT) { \
423  /* Is there enough room for more bytes? */ \
424  if (byte_buf - (uint8_t*)output_buf \
425  > *output_size) \
426  return PCOMP_STAT_INVALID_BUFFER; \
427  \
428  /* Flush "cur_byte_buf" into "output_buf" (this is \
429  * where byte_buf points to) */ \
430  *byte_buf++ = cur_byte_buf; \
431  \
432  bits_in_buffer = 0; \
433  cur_byte_buf = 0; \
434  } \
435  } \
436  } \
437  \
438  /* If there are still bytes in the cache, zero-fill the byte \
439  * and \
440  * flush it */ \
441  if (bits_in_buffer > 0) { \
442  *byte_buf++ = cur_byte_buf; \
443  } \
444  \
445  *output_size = byte_buf - (uint8_t*)output_buf; \
446  return PCOMP_STAT_SUCCESS; \
447  }
448 
449 DEFINE_COMPRESS_QUANT_FN(pcomp_compress_quant_float, find_bounds_float,
450  float)
451 DEFINE_COMPRESS_QUANT_FN(pcomp_compress_quant_double,
452  find_bounds_double, double)
453 
454 /***********************************************************************
455  * Quantization decompression functions
456  */
457 
458 #define DEFINE_DECOMPRESS_QUANT_FN(name, datatype_t) \
459  int name(datatype_t* output_buf, size_t output_size, \
460  const void* input_buf, size_t input_size, \
461  const pcomp_quant_params_t* params) \
462  { \
463  size_t output_idx; \
464  uint8_t byte; \
465  size_t bits_in_cache; \
466  const uint8_t* byte_buf = input_buf; \
467  \
468  if (output_buf == NULL || input_buf == NULL || params == NULL) \
469  abort(); \
470  \
471  if (input_size == 0) \
472  return PCOMP_STAT_SUCCESS; \
473  \
474  if (output_size == 0) \
475  return PCOMP_STAT_INVALID_BUFFER; \
476  \
477  byte = *byte_buf++; \
478  bits_in_cache = CHAR_BIT; \
479  \
480  output_idx = 0; \
481  while (output_idx < output_size) { \
482  size_t bit_idx; \
483  uint64_t buffer; \
484  \
485  buffer = 0; \
486  for (bit_idx = 0; bit_idx < params->bits_per_sample; \
487  ++bit_idx) { \
488  buffer = (buffer << 1) \
489  + ((uint64_t)byte >> (CHAR_BIT - 1)); \
490  byte <<= 1; \
491  bits_in_cache--; \
492  if (bits_in_cache == 0 && output_idx < output_size \
493  && (byte_buf - (uint8_t*)input_buf) \
494  < input_size) { \
495  byte = *byte_buf++; \
496  bits_in_cache = CHAR_BIT; \
497  } \
498  } \
499  \
500  output_buf[output_idx] = buffer / params->normalization \
501  + params->min_value; \
502  output_idx++; \
503  } \
504  \
505  return PCOMP_STAT_SUCCESS; \
506  }
507 
508 DEFINE_DECOMPRESS_QUANT_FN(pcomp_decompress_quant_float, float)
509 DEFINE_DECOMPRESS_QUANT_FN(pcomp_decompress_quant_double, double)
double pcomp_quant_normalization(const pcomp_quant_params_t *params)
Return the normalization constant used for converting floating-point numbers into quantized integers...
Definition: quant.c:170
size_t pcomp_quant_element_size(const pcomp_quant_params_t *params)
Return the size (in bytes) of the elements to be quantized.
Definition: quant.c:142
size_t pcomp_quant_bits_per_sample(const pcomp_quant_params_t *params)
Return the number of bits that must be used for each quantized sample.
Definition: quant.c:155
int pcomp_compress_quant_float(void *output_buf, size_t *output_size, const float *input_buf, size_t input_size, pcomp_quant_params_t *params)
Quantize a stream of 32-bit floating point numbers.
int pcomp_compress_quant_double(void *output_buf, size_t *output_size, const double *input_buf, size_t input_size, pcomp_quant_params_t *params)
Quantize a stream of 64-bit floating point numbers.
pcomp_quant_params_t * pcomp_init_quant_params(size_t element_size, size_t bits_per_sample)
Initialize a pcomp_quant_params_t structure.
Definition: quant.c:104
double pcomp_quant_offset(const pcomp_quant_params_t *params)
Return the additive constant used for converting floating-point numbers into quantized integers...
Definition: quant.c:185
size_t pcomp_quant_bufsize(size_t input_size, const pcomp_quant_params_t *params)
Return the size (in bytes) of the buffer that will contain a quantized stream of input_size floating ...
Definition: quant.c:244
void pcomp_quant_set_normalization(pcomp_quant_params_t *params, double normalization, double offset)
Set the normalization constants (multiplicative and additive) used to quantize floating-point numbers...
Definition: quant.c:212
void pcomp_free_quant_params(pcomp_quant_params_t *params)
Free a pcomp_quant_params_t structure.
Definition: quant.c:128
Header file for Libpolycomp.