Added the interfaces for json state output.
diff --git a/hex.c b/hex.c
index cc42430..d12daf2 100644
--- a/hex.c
+++ b/hex.c
@@ -1,24 +1,25 @@
#include "hex.h"
#include <stdio.h>
-hexdmp_error_t stderr_hexdmp(const char * description, const unsigned char * data_ptr, size_t datasz){
- if(description) {
- fprintf(stderr,"%s:\n", description);
+hexdmp_error_t stderr_hexdmp(const char* description,
+ const unsigned char* data_ptr, size_t datasz) {
+ if (description) {
+ fprintf(stderr, "%s:\n", description);
}
- if(datasz == 0) {
- fprintf(stderr,"No Data, Size is 0");
+ if (datasz == 0) {
+ fprintf(stderr, "No Data, Size is 0");
return HEXDMP_OK;
}
- if(!data_ptr) {
- fprintf(stderr,"No data pointer");
+ if (!data_ptr) {
+ fprintf(stderr, "No data pointer");
return HEXDMP_ERROR;
}
- for(size_t i = 0; i < datasz; i += 16) {
+ for (size_t i = 0; i < datasz; i += 16) {
// First print the offset.
- fprintf(stderr, "%08x", (unsigned) i);
+ fprintf(stderr, "%08x", (unsigned)i);
// Now print the next (at most) 16 characters.
- for(size_t j = 0; (j < 16) && (i+j) < datasz; j++) {
- fprintf(stderr, " %02x", data_ptr[i+j]);
+ for (size_t j = 0; (j < 16) && (i + j) < datasz; j++) {
+ fprintf(stderr, " %02x", data_ptr[i + j]);
}
// Print a newline.
fprintf(stderr, "\n");
diff --git a/hex.h b/hex.h
index d47281e..432ca26 100644
--- a/hex.h
+++ b/hex.h
@@ -1,11 +1,12 @@
#ifndef __MEDIAN_HEX_H_INCLUDED__
#define __MEDIAN_HEX_H_INCLUDED__
-#include <strings.h> // size_t
+#include <strings.h> // size_t
typedef int hexdmp_error_t;
static const hexdmp_error_t HEXDMP_OK = 0;
static const hexdmp_error_t HEXDMP_ERROR = -1;
// The function produces an output to stderr similar to the "hexdump"
// application. Useful in debugging things.
-hexdmp_error_t stderr_hexdmp(const char * description, const unsigned char * data_ptr, size_t datasz);
-#endif // __MEDIAN_HEX_H_INCLUDED__
+hexdmp_error_t stderr_hexdmp(const char* description,
+ const unsigned char* data_ptr, size_t datasz);
+#endif // __MEDIAN_HEX_H_INCLUDED__
diff --git a/median.c b/median.c
index 1162fbd..e47bb61 100644
--- a/median.c
+++ b/median.c
@@ -1,10 +1,10 @@
#include "median.h"
+#include <pthread.h> // pthread_mutex_*
+#include <stdio.h> // fprintf
+#include <stdlib.h> // qsort
+#include <string.h> // memcpy
+#include <strings.h> // size_t
#include "hex.h"
-#include <strings.h> // size_t
-#include <stdio.h> // fprintf
-#include <stdlib.h> // qsort
-#include <string.h> // memcpy
-#include <pthread.h> // pthread_mutex_*
// The structure of the data buffer is this:
// ----------------------------------------------------------------------------
@@ -14,107 +14,125 @@
// is stored in Metadata as well.
// The buffer is cast into this struct.
-
// ----------------------------------------------------------------------------
// The block header.
// ----------------------------------------------------------------------------
-typedef struct blockdata {
- unsigned int id; // Block identity.
- size_t level; // The level of this buffer.
- size_t occupancy; // Number of occupants for this buffer.
- unsigned char data[0]; // Start of the data block, cast as unsigned char.
- median_data_t median_data[0]; // The data for this buffer cast as median_data_t.
-} blockdata_t;
+struct blockdata {
+ unsigned int id; // Block identity.
+ size_t level; // The level of this buffer.
+ size_t occupancy; // Number of occupants for this buffer.
+ unsigned char data[0]; // Start of the data block, cast as unsigned char.
+ median_data_t
+ median_data[0]; // The data for this buffer cast as median_data_t.
+};
// ----------------------------------------------------------------------------
// The overall header
// ----------------------------------------------------------------------------
-typedef struct metadata {
- pthread_mutex_t mutex_ptr[0]; // Pointer to the mutex.
- pthread_mutex_t mutex; // The mutex for this median counter.
- unsigned int gid; // The next available global id
- size_t nLevels; // Number of buffers available.
- size_t nMembers; // The number of members in each block.
- size_t currentBufferIdx; // The index of the currently active buffer.
- unsigned char data[0]; // The start of the data.
- blockdata_t block_data[0]; // Makes the code cleaner and avoids unnecessary casting.
-} metadata_t;
+struct metadata {
+ pthread_mutex_t mutex_ptr[0]; // Pointer to the mutex.
+ pthread_mutex_t mutex; // The mutex for this median counter.
+ unsigned int gid; // The next available global id
+ size_t nLevels; // Number of buffers available.
+ size_t nMembers; // The number of members in each block.
+ size_t currentBufferIdx; // The index of the currently active buffer.
+ unsigned char data[0]; // The start of the data.
+ struct blockdata block_data[0]; // Makes the code cleaner and avoids
+ // unnecessary casting.
+};
+
+// ----------------------------------------------------------------------------
+// These structs are used internally for algorithmic work.
+//
+//
// ----------------------------------------------------------------------------
// The record used for sorting the output
// ----------------------------------------------------------------------------
-typedef struct sortrec {
- median_data_t d; // The data point.
- size_t sz; // The leveled size of the point.
-} sortrec_t;
+struct sortrec {
+ median_data_t d; // The data point.
+ size_t sz; // The leveled size of the point.
+};
// ----------------------------------------------------------------------------
-// The spare buffer space header.
+// The spare buffer header.
// ----------------------------------------------------------------------------
-typedef struct spare_buffer {
- size_t spareSize; // Size in bytes of the spare block.
- unsigned char data[0]; // Start of the data block.
- sortrec_t sortrec_data[0]; // The same point cast as a sortrec
- median_data_t median_data[0]; // The same point cast as a median datum
- size_t size_data[0]; // The same point case as size_t
-} spare_buffer_t;
+struct spare_buffer {
+ size_t spareSize; // Size in bytes of the spare block.
+ unsigned char data[0]; // Start of the data block.
+ struct sortrec sortrec_data[0]; // The same point cast as a sortrec
+ median_data_t median_data[0]; // The same point cast as a median datum
+ size_t size_data[0]; // The same point case as size_t
+};
+// ----------------------------------------------------------------------------
+// This is the struct used to hold a JSON output string.
+// ----------------------------------------------------------------------------
+// A buffer to hold the output string.
+// ----------------------------------------------------------------------------
+struct output_buffer {
+ size_t outputBufferSize; // The buffer size in char
+ char mesg[0]; // The message.
+};
// ----------------------------------------------------------------------------
// Helper functions.
//
// This turns epsilon into a suggested size for each block.
size_t calculate_nMembers(double epsilon) {
- return (size_t) ((1.0 / epsilon) + 1);
+ return (size_t)((1.0 / epsilon) + 1);
}
// This turns maxN into the number of levels needed. It's ceiling(log2(maxN)).
size_t calculate_nLevels(size_t maxN) {
size_t nLevels = 1;
- while((maxN = (maxN >> 1))) nLevels++;
+ while ((maxN = (maxN >> 1))) nLevels++;
return nLevels;
}
// This returns the blocksize in bytes.
-// The blocksize is the header plus m->nMembers many
+// The blocksize is the header plus m->nMembers many
// data points, each of type median_data_t
-size_t blocksize(metadata_t * m) {
- return sizeof(blockdata_t) + m->nMembers * sizeof(median_data_t);
+size_t blocksize(struct metadata *m) {
+ return sizeof(struct blockdata) + m->nMembers * sizeof(median_data_t);
}
// This return the start of the next block given
-// a pointer to a block. The easiest way is to jump
+// a pointer to a block. The easiest way is to jump
// m->nMembers median_data_t items away.
-blockdata_t * nextblock(blockdata_t * blk, metadata_t * m) {
- return (blockdata_t *) (blk->median_data + m->nMembers);
+struct blockdata *nextblock(struct blockdata *blk, struct metadata *m) {
+ return (struct blockdata *)(blk->median_data + m->nMembers);
}
// This returns the size of the entire buffer in bytes.
// The size of the metadata block, plus m->nLevels many
// blocks each of size blocksize(m)
-size_t buffersize(metadata_t * m) {
- return sizeof(metadata_t) + m->nLevels * blocksize(m);
+size_t buffersize(struct metadata *m) {
+ return sizeof(struct metadata) + m->nLevels * blocksize(m);
}
// Returns the first byte address which is too high to start a block regardless
// of size.. One blockdata worth less than the actual end of the buffer plus
-// one. The computation is done in byte units and then cast as a blockdata_t
-// pointer.
-blockdata_t * out_of_bounds(metadata_t * m) {
- return (blockdata_t *) (m->data + buffersize(m) - sizeof(metadata_t) - blocksize(m));
+// one. The computation is done in byte units and then cast as a struct
+// blockdata pointer.
+struct blockdata *out_of_bounds(struct metadata *m) {
+ return (struct blockdata *)(m->data + buffersize(m) -
+ sizeof(struct metadata) - blocksize(m));
}
// Get the current block.
-// The computation is done in bytes, and then cast as a blockdata_t pointer.
-blockdata_t * get_current_block(metadata_t *m) {
+// The computation is done in bytes, and then cast as a struct blockdata
+// pointer.
+struct blockdata *get_current_block(struct metadata *m) {
size_t offset = m->currentBufferIdx * blocksize(m);
- return (blockdata_t *)(m->data + offset);
+ return (struct blockdata *)(m->data + offset);
}
// This function inserts data into the current block if possible.
// Otherwise, it returns false.
-unsigned int insert_into_current_block_if_possible(metadata_t *m, median_data_t data) {
- blockdata_t * b = get_current_block(m);
+unsigned int insert_into_current_block_if_possible(struct metadata *m,
+ median_data_t data) {
+ struct blockdata *b = get_current_block(m);
if (b->occupancy < m->nMembers) {
b->median_data[b->occupancy++] = data;
return 1;
@@ -124,10 +142,11 @@
// Check if there is space in some block and if there is, then
// make that block the current block.
-unsigned int occupy_available_empty_block(metadata_t * m) {
+unsigned int occupy_available_empty_block(struct metadata *m) {
// Iterate over the blocks.
- for(blockdata_t * b = m->block_data; b < out_of_bounds(m); b = nextblock(b,m)) {
- if(b->occupancy < m->nMembers) {
+ for (struct blockdata *b = m->block_data; b < out_of_bounds(m);
+ b = nextblock(b, m)) {
+ if (b->occupancy < m->nMembers) {
// Empty block found!
m->currentBufferIdx = b->id;
return 1;
@@ -138,20 +157,21 @@
}
// A simple compare operator for the qsort routine.
-int compare_data(const void *a, const void *b){
- median_data_t * i = (median_data_t *)a;
- median_data_t * j = (median_data_t *)b;
- return (*i < *j)?-1:+1;
+int compare_data(const void *a, const void *b) {
+ median_data_t *i = (median_data_t *)a;
+ median_data_t *j = (median_data_t *)b;
+ return (*i < *j) ? -1 : +1;
}
-unsigned int merge_blocks(metadata_t *m,blockdata_t *i,blockdata_t *j, median_scratch_buffer_t spare){
+unsigned int merge_blocks(struct metadata *m, struct blockdata *i,
+ struct blockdata *j, median_scratch_buffer_t spare) {
// Prepare an array for the data.
- unsigned char * d = spare->data;
+ unsigned char *d = spare->data;
size_t datasz = m->nMembers * sizeof(median_data_t);
- if (2*datasz > spare->spareSize) {
+ if (2 * datasz > spare->spareSize) {
return 0;
}
- bzero(d, 2*datasz);
+ bzero(d, 2 * datasz);
// This is the size of the data.
// Copy the data to where it needs to be.
@@ -165,7 +185,8 @@
// be consuming level 0 buffers, I have left this
// inefficiency in.
// But we should fix it.
- qsort(spare->median_data,2*m->nMembers,sizeof(median_data_t),&compare_data);
+ qsort(spare->median_data, 2 * m->nMembers, sizeof(median_data_t),
+ &compare_data);
// Now interleave the sorted buffer.
// We should track parity and take the
@@ -176,8 +197,8 @@
// zero or one.
// However, this does make the routine somewhat non
// deterministic, but offers some extra accuracy.
- for(size_t idx = 0; idx < 2*m->nMembers; idx+=2){
- i->median_data[idx/2] = spare->median_data[idx];
+ for (size_t idx = 0; idx < 2 * m->nMembers; idx += 2) {
+ i->median_data[idx / 2] = spare->median_data[idx];
}
// Update the metadata. i is full, j is empty.
@@ -194,7 +215,8 @@
}
// Find the smallest level such that there are two blocks at the same level.
-unsigned int identify_qualified_level(metadata_t *m, size_t *lvl, median_scratch_buffer_t spare) {
+unsigned int identify_qualified_level(struct metadata *m, size_t *lvl,
+ median_scratch_buffer_t spare) {
// Zero out the count buffer after validating the
// spare space buffer.
size_t datasz = m->nLevels * sizeof(size_t);
@@ -203,21 +225,22 @@
}
// Prepare the size array.
- size_t * levelcount = spare->size_data;
+ size_t *levelcount = spare->size_data;
bzero(levelcount, datasz);
// Count up the level population.
- for(blockdata_t * b = m->block_data; b < out_of_bounds(m); b = nextblock(b,m)) {
+ for (struct blockdata *b = m->block_data; b < out_of_bounds(m);
+ b = nextblock(b, m)) {
levelcount[b->level]++;
}
// Look for a level where the count is at least 2.
- for(size_t idx = 0; idx < m->nLevels; idx++) {
- if(levelcount[idx] > 1) {
- // Found one, return it.
- *lvl = idx;
- return 1;
- }
+ for (size_t idx = 0; idx < m->nLevels; idx++) {
+ if (levelcount[idx] > 1) {
+ // Found one, return it.
+ *lvl = idx;
+ return 1;
+ }
}
// There is no such thing.
@@ -225,11 +248,14 @@
}
// This identifies the pair of blocks to merge.
-unsigned int identify_compatible_block_pair(metadata_t *m, blockdata_t ** i, blockdata_t ** j, median_scratch_buffer_t spare) {
+unsigned int identify_compatible_block_pair(struct metadata *m,
+ struct blockdata **i,
+ struct blockdata **j,
+ median_scratch_buffer_t spare) {
// The qualified level will reside here.
// Find the qualified level.
size_t qualified_level = 0;
- if (!identify_qualified_level(m,&qualified_level,spare)) {
+ if (!identify_qualified_level(m, &qualified_level, spare)) {
return 0;
}
@@ -239,48 +265,52 @@
// State machine to find the two buffers.
// Iterate over the blocks.
- for(blockdata_t * b = m->block_data; b < out_of_bounds(m); b = nextblock(b,m)) {
+ for (struct blockdata *b = m->block_data; b < out_of_bounds(m);
+ b = nextblock(b, m)) {
if (!(*i) && (b->level == qualified_level)) {
- *i = b; continue; // Found the first one.
+ *i = b;
+ continue; // Found the first one.
}
if (!(*j) && (b->level == qualified_level)) {
- *j = b; return 1; // Found the second one.
+ *j = b;
+ return 1; // Found the second one.
}
}
// Loop should not exit before the two are found.
return 0;
}
-
// THis routine creates an empty block by merging two available blocks into a
// single block. Consequently this frees up one of them.
// If two compatible blocks cannot be found, then this routine returns 0.
// It also returns 0 if for some reason the two blocks cannot be merged.
-unsigned int create_empty_block(metadata_t * m, median_scratch_buffer_t spare) {
- blockdata_t *i,*j;
- if(!identify_compatible_block_pair(m, &i, &j, spare)){
+unsigned int create_empty_block(struct metadata *m,
+ median_scratch_buffer_t spare) {
+ struct blockdata *i, *j;
+ if (!identify_compatible_block_pair(m, &i, &j, spare)) {
return 0;
}
- return merge_blocks(m,i,j,spare);
+ return merge_blocks(m, i, j, spare);
}
// Shift the current block. If there is an empty block, great!
// If not, then empty a block by merging two blocks and then occupy the
// freed up block.
-unsigned int shift_current_block(metadata_t * m, median_scratch_buffer_t spare) {
- if(!occupy_available_empty_block(m)) {
- if(create_empty_block(m,spare)) { // Now there should be an empty block!
- if(!occupy_available_empty_block(m)) {
- return 0; // Stuck we are. Empty block is not, even though create it we did.
+unsigned int shift_current_block(struct metadata *m,
+ median_scratch_buffer_t spare) {
+ if (!occupy_available_empty_block(m)) {
+ if (create_empty_block(m, spare)) { // Now there should be an empty block!
+ if (!occupy_available_empty_block(m)) {
+ return 0; // Stuck we are. Empty block is not, even though create it we
+ // did.
}
} else {
- return 0; // Stuck we are. Block not.
+ return 0; // Stuck we are. Block not.
}
}
- return 1; // Occupy a block we did.
+ return 1; // Occupy a block we did.
}
-
// ----------------------------------------------------------------------------
// Implementation of the interface functions (specified in median.h).
// ----------------------------------------------------------------------------
@@ -288,19 +318,22 @@
// ----------------------------------------------------------------------------
// Suggest the size of the buffer needed for a fixed epsilon and maxN.
// ----------------------------------------------------------------------------
-median_error_t median_suggest_buffer_size(double epsilon, size_t maxN, size_t *suggested_size){
+median_error_t median_suggest_buffer_size(double epsilon, size_t maxN,
+ size_t *suggested_size) {
// First check for sane parameters.
// Check that maxN is not zero.
if (maxN <= 0) return MEDIAN_ERROR_INVALID_PARAMETERS;
- // If n is too large, (i.e. epsilon is too small), you are looking for too fine an approximation.
- // This is not the right algorithm. Maybe consider random sampling instead.
- if (calculate_nMembers(epsilon) > 100000) return MEDIAN_ERROR_INVALID_PARAMETERS;
+ // If n is too large, (i.e. epsilon is too small), you are looking for too
+ // fine an approximation. This is not the right algorithm. Maybe consider
+ // random sampling instead.
+ if (calculate_nMembers(epsilon) > 100000)
+ return MEDIAN_ERROR_INVALID_PARAMETERS;
// Calculate the required buffer size for the right nLevels and nMembers
// value.
- metadata_t m;
+ struct metadata m;
m.nLevels = calculate_nLevels(maxN);
m.nMembers = calculate_nMembers(epsilon);
@@ -308,7 +341,7 @@
// and outputing. If we have many ongoing buffers, we can do with a single
// spare space buffer. We can figure out that optimization later.
// Ideally, this will be done by keeping a pointer to the global spare space
- // within the metadata_t block. I prefer keeping this data structure
+ // within the struct metadata block. I prefer keeping this data structure
// relocatable and serializable. So we may need to think about what the
// alternatives are.
*suggested_size = buffersize(&m);
@@ -320,25 +353,42 @@
// ----------------------------------------------------------------------------
// Suggest a buffer size for the scratch buffer.
// ----------------------------------------------------------------------------
-median_error_t median_suggest_scratch_buffer_size(median_buffer_t m, size_t *suggested_size){
- *suggested_size = m->nLevels * m->nMembers * sizeof(sortrec_t) + sizeof(spare_buffer_t);
+median_error_t median_suggest_scratch_buffer_size(median_buffer_t m,
+ size_t *suggested_size) {
+ *suggested_size = m->nLevels * m->nMembers * sizeof(struct sortrec) +
+ sizeof(struct spare_buffer);
+ return MEDIAN_OK;
+}
+
+// ----------------------------------------------------------------------------
+// Suggest a size of the output buffer.
+// ----------------------------------------------------------------------------
+median_error_t median_suggest_json_string_size(median_buffer_t m,
+ size_t *suggested_size) {
+ // ToDo improve this estimate.
+ *suggested_size = 256 * 1024; // Constant size of 256K for now.
return MEDIAN_OK;
}
// ----------------------------------------------------------------------------
// Initialize an allocated buffer.
// ----------------------------------------------------------------------------
-median_error_t median_init_buffer(void * buffer, size_t buffer_size, double epsilon, size_t maxN, median_buffer_t *initialized_buffer){
+median_error_t median_init_buffer(void *buffer, size_t buffer_size,
+ double epsilon, size_t maxN,
+ median_buffer_t *initialized_buffer) {
// Initialize.
size_t expectedSize = 0;
median_error_t error = MEDIAN_OK;
// First run median_suggest_buffer_size
- if ((error = median_suggest_buffer_size(epsilon,maxN,&expectedSize)) != MEDIAN_OK) return error;
+ if ((error = median_suggest_buffer_size(epsilon, maxN, &expectedSize)) !=
+ MEDIAN_OK)
+ return error;
// Ensure that the buffer is large enough.
if (expectedSize > buffer_size) return MEDIAN_ERROR_BUFFER_TOO_SMALL;
- // Cast the buffer as metadata_t so that we can initialize the metadata block.
- metadata_t * m = (metadata_t *)buffer;
+ // Cast the buffer as struct metadata so that we can initialize the metadata
+ // block.
+ struct metadata *m = (struct metadata *)buffer;
// Initialize the mutex.
pthread_mutex_init(m->mutex_ptr, NULL);
// Initialize the metadata block.
@@ -348,14 +398,15 @@
m->currentBufferIdx = 0;
// Zero all the data following the metadata block.
// Strictly this should not be necessary, but I'm a coward!
- bzero(m->data, buffersize(m) - sizeof(metadata_t));
+ bzero(m->data, buffersize(m) - sizeof(struct metadata));
- // cast the data part as an array of blockdata_t fronted blocks.
+ // cast the data part as an array of struct blockdata fronted blocks.
// and initialize each of the blocks.
// Iterate over the blocks.
- for(blockdata_t * b = m->block_data; b < out_of_bounds(m); b = nextblock(b,m)) {
+ for (struct blockdata *b = m->block_data; b < out_of_bounds(m);
+ b = nextblock(b, m)) {
b->id = (m->gid)++;
- b->level = 0;
+ b->level = 0;
b->occupancy = 0;
}
@@ -370,33 +421,52 @@
return MEDIAN_OK;
}
+// Nothing to do, yet!
+median_error_t median_deinit_scratch_buffer(median_scratch_buffer_t s) {
+ return MEDIAN_OK;
+}
+
+// Nothing to do, yet!
+median_error_t median_deinit_json_string(median_json_string_t o) {
+ return MEDIAN_OK;
+}
// ----------------------------------------------------------------------------
// Initialize the scratch buffer.
// ----------------------------------------------------------------------------
-median_error_t median_init_scratch_buffer(void * buffer, size_t buffer_size, median_buffer_t m, median_scratch_buffer_t * initialized_buffer){
+median_error_t median_init_scratch_buffer(
+ void *buffer, size_t buffer_size, median_buffer_t m,
+ median_scratch_buffer_t *initialized_buffer) {
median_scratch_buffer_t s = buffer;
- s->spareSize = buffer_size - sizeof(spare_buffer_t);
+ s->spareSize = buffer_size - sizeof(struct spare_buffer);
*initialized_buffer = s;
return MEDIAN_OK;
}
+median_error_t median_init_json_string(void *buffer, size_t buffer_size,
+ median_buffer_t m,
+ median_json_string_t *output_buffer) {
+ median_json_string_t o = buffer;
+ o->outputBufferSize = buffer_size - sizeof(struct output_buffer);
+ *output_buffer = o;
+ return MEDIAN_OK;
+}
+
// ----------------------------------------------------------------------------
// Insert data into the buffer.
// ----------------------------------------------------------------------------
// Unprotected function.
-median_error_t _median_insert_data(median_buffer_t m,
- median_data_t data,
- median_scratch_buffer_t scratch){
+median_error_t _median_insert_data(median_buffer_t m, median_data_t data,
+ median_scratch_buffer_t scratch) {
// If there is space in the current block..
- if(!insert_into_current_block_if_possible(m,data)) {
+ if (!insert_into_current_block_if_possible(m, data)) {
// Shift
- if(!shift_current_block(m,scratch)) {
+ if (!shift_current_block(m, scratch)) {
return MEDIAN_ERROR_MAX_N_EXCEEDED;
}
// Try to insert again, this should work because
// we just shifted.
- if(!insert_into_current_block_if_possible(m,data)) {
+ if (!insert_into_current_block_if_possible(m, data)) {
return MEDIAN_ERROR_BUG;
}
}
@@ -404,13 +474,12 @@
}
// Locked function.
-median_error_t median_insert_data(median_buffer_t m,
- median_data_t data,
- median_scratch_buffer_t scratch){
+median_error_t median_insert_data(median_buffer_t m, median_data_t data,
+ median_scratch_buffer_t scratch) {
// Lock.
pthread_mutex_lock(m->mutex_ptr);
// Call the unprotected routine.
- median_error_t ret = _median_insert_data(m,data,scratch);
+ median_error_t ret = _median_insert_data(m, data, scratch);
// Unlock
pthread_mutex_unlock(m->mutex_ptr);
// return
@@ -420,8 +489,8 @@
// ----------------------------------------------------------------------------
// Compare two sort records.
// ----------------------------------------------------------------------------
-int compare_sortrec(const void * a, const void *b) {
- return ((sortrec_t *)a)->d < ((sortrec_t*)b)->d?-1:1;
+int compare_sortrec(const void *a, const void *b) {
+ return ((struct sortrec *)a)->d < ((struct sortrec *)b)->d ? -1 : 1;
}
// ----------------------------------------------------------------------------
@@ -430,37 +499,38 @@
// Unprotected implementation.
median_error_t _median_output_median(const median_buffer_t m,
- median_data_t *approximate_median,
- median_scratch_buffer_t scratch){
+ median_data_t *approximate_median,
+ median_scratch_buffer_t scratch) {
// Initialize the data buffer to zero.
- size_t datasz = m->nMembers * m->nLevels * sizeof(sortrec_t);
- if(datasz > scratch->spareSize) {
+ size_t datasz = m->nMembers * m->nLevels * sizeof(struct sortrec);
+ if (datasz > scratch->spareSize) {
return MEDIAN_ERROR_BUFFER_TOO_SMALL;
}
- sortrec_t * s = scratch->sortrec_data;
+ struct sortrec *s = scratch->sortrec_data;
bzero(s, datasz);
// Initialize the count and weight values.
size_t count = 0;
size_t weight = 0;
// First construct the sort buffer.
- for(blockdata_t * b = m->block_data; b < out_of_bounds(m); b = nextblock(b,m)) {
- for(size_t i = 0; i < b->occupancy; i++) {
+ for (struct blockdata *b = m->block_data; b < out_of_bounds(m);
+ b = nextblock(b, m)) {
+ for (size_t i = 0; i < b->occupancy; i++) {
s->d = b->median_data[i];
s->sz = (1 << b->level);
weight += s->sz;
- s++; count++;
+ s++;
+ count++;
}
}
- qsort(scratch->sortrec_data,count,sizeof(sortrec_t),&compare_sortrec);
+ qsort(scratch->sortrec_data, count, sizeof(struct sortrec), &compare_sortrec);
// This is the weight at which the median will be found.
- weight = weight/2;
+ weight = weight / 2;
// We now need to pick out the
// median.
- for(s = scratch->sortrec_data;
- weight > s->sz;
- weight -= (s++)->sz);
+ for (s = scratch->sortrec_data; weight > s->sz; weight -= (s++)->sz)
+ ;
*approximate_median = s->d;
return MEDIAN_OK;
@@ -469,27 +539,44 @@
// Locked call.
median_error_t median_output_median(const median_buffer_t m,
median_data_t *approximate_median,
- median_scratch_buffer_t scratch){
+ median_scratch_buffer_t scratch) {
pthread_mutex_lock(m->mutex_ptr);
median_error_t ret = _median_output_median(m, approximate_median, scratch);
pthread_mutex_unlock(m->mutex_ptr);
return ret;
}
+
+median_error_t _median_output_json(const median_buffer_t m,
+ median_json_string_t output) {
+ return MEDIAN_OK;
+}
+median_error_t median_output_json(const median_buffer_t m,
+ median_json_string_t output) {
+ pthread_mutex_lock(m->mutex_ptr);
+ median_error_t ret = _median_output_json(m, output);
+ pthread_mutex_unlock(m->mutex_ptr);
+ return ret;
+}
// ----------------------------------------------------------------------------
// Dump the state to stderr.
// ----------------------------------------------------------------------------
// Unprotected call.
-median_error_t _median_dump_stderr(const median_buffer_t m){
+median_error_t _median_dump_stderr(const median_buffer_t m) {
// First print out all the metadata.
- blockdata_t * current = get_current_block(m);
- fprintf(stderr, "Metadata:\n Next GID:%d\n nMembers:%d\n nLevels:%d\n CurrentBlock:%d\n",m->gid,(unsigned)m->nMembers,(unsigned)m->nLevels, current->id);
+ struct blockdata *current = get_current_block(m);
+ fprintf(
+ stderr,
+ "Metadata:\n Next GID:%d\n nMembers:%d\n nLevels:%d\n CurrentBlock:%d\n",
+ m->gid, (unsigned)m->nMembers, (unsigned)m->nLevels, current->id);
- for(blockdata_t * b = m->block_data; b < out_of_bounds(m); b = nextblock(b,m)) {
- // Now dump the blocks.
- fprintf(stderr, "Block:%d\n Level:%d\n Occupancy:%d\n", b->id,(unsigned)b->level,(unsigned)b->occupancy);
+ for (struct blockdata *b = m->block_data; b < out_of_bounds(m);
+ b = nextblock(b, m)) {
+ // Now dump the blocks.
+ fprintf(stderr, "Block:%d\n Level:%d\n Occupancy:%d\n", b->id,
+ (unsigned)b->level, (unsigned)b->occupancy);
// For every block, dump the first 8 keys in the block.
- stderr_hexdmp("Data:",b->data,8*sizeof(median_data_t));
+ stderr_hexdmp("Data:", b->data, 8 * sizeof(median_data_t));
}
// Done, return MEDIAN_OK.
@@ -497,7 +584,7 @@
}
// Protected call.
-median_error_t median_dump_stderr(const median_buffer_t m){
+median_error_t median_dump_stderr(const median_buffer_t m) {
pthread_mutex_lock(m->mutex_ptr);
median_error_t ret = _median_dump_stderr(m);
pthread_mutex_unlock(m->mutex_ptr);
diff --git a/median.h b/median.h
index 5e02bf8..d4a83a2 100644
--- a/median.h
+++ b/median.h
@@ -1,7 +1,7 @@
#ifndef __MONITORING_MEDIAN_H_INCLUDED__
#define __MONITORING_MEDIAN_H_INCLUDED__
-#include <stdint.h>
#include <stddef.h>
+#include <stdint.h>
// ------------------------------------------------------------------------
// The core phylosophy of the interface is this.
@@ -19,14 +19,12 @@
// The ErrorCode returned by all
// functions in this file.
typedef int32_t median_error_t;
-
-struct metadata_t;
-struct spare_buffer_t;
// A buffer which contains all the state held
// within an approximate median calculator.
// And another type for scratch data.
-typedef struct metadata * median_buffer_t;
-typedef struct spare_buffer * median_scratch_buffer_t;
+typedef struct metadata *median_buffer_t;
+typedef struct spare_buffer *median_scratch_buffer_t;
+typedef struct output_buffer *median_json_string_t;
// A single data element. For now this is defined
// to be a 64 bit integer. But in principle this could be
@@ -42,59 +40,74 @@
static const median_error_t MEDIAN_ERROR_INVALID_PARAMETERS = -1;
// The buffer allocated is too small for the parameters requested.
-static const median_error_t MEDIAN_ERROR_BUFFER_TOO_SMALL = -2;
+static const median_error_t MEDIAN_ERROR_BUFFER_TOO_SMALL = -2;
// Overflow because MaxN has been exceeded.
-static const median_error_t MEDIAN_ERROR_MAX_N_EXCEEDED = -3;
+static const median_error_t MEDIAN_ERROR_MAX_N_EXCEEDED = -3;
// This is returned when some invariant is broken.
// It should not happen in normal operation.
-static const median_error_t MEDIAN_ERROR_BUG = -1000;
+static const median_error_t MEDIAN_ERROR_BUG = -1000;
// Indicates that the size of the largest possible dataset is not known.
static const size_t MEDIAN_MAX_N_UNKNOWN = 0;
// The default size of the largest possible dataset.
-static const size_t MEDIAN_MAX_N_DEFAULT = 1024*1024*1024; // 1G.
+static const size_t MEDIAN_MAX_N_DEFAULT = 1024 * 1024 * 1024; // 1G.
// This routine returns a suggested size for a buffer to hold all the
// state needed for a median calculation. Here epsilon is the permissible error.
// maxN is the largest dataset that we anticipate. If maxN is set to
-// MEDIAN_MAX_N_UNKNOWN (or zero), then we assume MEDIAN_MAX_N_DEFAULT to be the value of
-// maxN. The value return parameter is conventionally the last one. In this
-// case, it contains the suggested_size of the buffer, assuming that the return
-// value is MEDIAN_ERROR_OK.
-// In all reasonable calls, the returned value will be
-// MEDIAN_ERROR_OK.
-median_error_t median_suggest_buffer_size(double epsilon, size_t maxN, size_t *suggested_size);
+// MEDIAN_MAX_N_UNKNOWN (or zero), then we assume MEDIAN_MAX_N_DEFAULT to be the
+// value of maxN. The value return parameter is conventionally the last one. In
+// this case, it contains the suggested_size of the buffer, assuming that the
+// return value is MEDIAN_ERROR_OK. In all reasonable calls, the returned value
+// will be MEDIAN_ERROR_OK.
+median_error_t median_suggest_buffer_size(double epsilon, size_t maxN,
+ size_t *suggested_size);
// Suggest a size of the buffer for scratch space. This is needed
// so that the buffer is not corrupted by the output and merge steps.
-median_error_t median_suggest_scratch_buffer_size(median_buffer_t m, size_t *suggested_size);
+median_error_t median_suggest_scratch_buffer_size(median_buffer_t m,
+ size_t *suggested_size);
+// Suggest a size of a buffer to output a JSON description of the median status.
+// This is the function that is used to serialize out the result when needed.
+median_error_t median_suggest_json_string_size(median_buffer_t m,
+ size_t *suggested_size);
// This routine will init the buffer. It will check if the buffer meets the
// size restriction returned by #median_suggest_buffer_size(). If not, it will
// exit with MEDIAN_ERROR_BUFFER_TOO_SMALL.
// The method can also exit with MEDIAN_ERR)R_INVALID_PARAMETERS in case there
// are some meaningless parameters passed in the call.
-// We assume that a buffer of length buffer_size (in bytes) has been allocated and is
-// available at the pointer specified by buffer.
-// The parameters to median_suggest_buffer_size are passed into the data
-// initialization routine, and the init routine will store them inside the data
-// structure, so that the values can be used during the operation
-// of the data structure.
-// The value return parameter initialized_buffer is identical to the buffer
-// variable (i.e. *initialized_buffer == buffer), but is appropriately cast
-// to indicate that it is initialized.
-// In all reasonable calls, the returned value will be
+// We assume that a buffer of length buffer_size (in bytes) has been allocated
+// and is available at the pointer specified by buffer. The parameters to
+// median_suggest_buffer_size are passed into the data initialization routine,
+// and the init routine will store them inside the data structure, so that the
+// values can be used during the operation of the data structure. The value
+// return parameter initialized_buffer is identical to the buffer variable (i.e.
+// *initialized_buffer == buffer), but is appropriately cast to indicate that it
+// is initialized. In all reasonable calls, the returned value will be
// MEDIAN_ERROR_OK.
-median_error_t median_init_buffer(void * buffer, size_t buffer_size, double epsilon, size_t maxN, median_buffer_t *initialized_buffer);
+median_error_t median_init_buffer(void *buffer, size_t buffer_size,
+ double epsilon, size_t maxN,
+ median_buffer_t *initialized_buffer);
// This frees up any internal resource consumed with the buffer, including
// mutex locks and other provisioned resources.
median_error_t median_deinit_buffer(median_buffer_t m);
// Initialize the scratch buffer.
// This function is similar in behaviour to the
// init function for the main buffer.
-median_error_t median_init_scratch_buffer(void * buffer, size_t buffer_size, median_buffer_t m, median_scratch_buffer_t * initialized_buffer);
+median_error_t median_init_scratch_buffer(
+ void *buffer, size_t buffer_size, median_buffer_t m,
+ median_scratch_buffer_t *initialized_buffer);
+// De-init the scratch buffer.
+median_error_t median_deinit_scratch_buffer(median_scratch_buffer_t s);
+// Initialize the output buffer.
+median_error_t median_init_json_string(
+ void *buffer, size_t buffer_size, median_buffer_t m,
+ median_json_string_t *initialized_buffer);
+// De-init the output buffer.
+median_error_t median_deinit_json_string(median_json_string_t b);
// This routine inserts a data point into the median computation. It's
// implemented to be really cheap. However, it is not always constant time.
@@ -111,7 +124,8 @@
// then MEDIAN_ERROR_INVALID_PARAMETERS will be returned.
// In all reasonable calls, the returned value will be
// MEDIAN_ERROR_OK.
-median_error_t median_insert_data(median_buffer_t buffer, median_data_t data, median_scratch_buffer_t scratch_buffer);
+median_error_t median_insert_data(median_buffer_t buffer, median_data_t data,
+ median_scratch_buffer_t scratch_buffer);
// This routine returns the approximate median of the data seen thusfar.
// This is implemented to be really cheap as well, but not as cheap as the
@@ -126,9 +140,14 @@
// return an error.
// If it does return an error, the error will be
// MEDIAN_ERROR_INVALID_PARAMETERS
-median_error_t median_output_median(const median_buffer_t buffer, median_data_t *approximate_median, median_scratch_buffer_t scratch_buffer);
-
-// This hexdumps the state of the buffer to stderr so that it can help debug if needed.
+median_error_t median_output_median(const median_buffer_t buffer,
+ median_data_t *approximate_median,
+ median_scratch_buffer_t scratch_buffer);
+// Output the JSON representation of the median buffer state.
+median_error_t median_output_json(const median_buffer_t buffer,
+ median_json_string_t output);
+// This hexdumps the state of the buffer to stderr so that it can help debug if
+// needed.
median_error_t median_dump_stderr(const median_buffer_t buffer);
-#endif // ._MONITORING_MEDIAN>H_INCLUDED__
+#endif // ._MONITORING_MEDIAN>H_INCLUDED__
diff --git a/median.main.c b/median.main.c
index 32beae4..15cbb36 100644
--- a/median.main.c
+++ b/median.main.c
@@ -1,34 +1,36 @@
-#include <stdio.h> // fprintf, stderr
-#include <stdlib.h> // size_t
-#include <assert.h> // assert
-#include "median.h"
+#include <assert.h> // assert
+#include <stdio.h> // fprintf, stderr
+#include <stdlib.h> // size_t
#include "hex.h"
+#include "median.h"
-#define TEST_SIZE 1024*1024*256 // 256M
+#define TEST_SIZE 1024 * 1024 * 256 // 256M
// A sinple main routine meant as an example,
// and also to unit test most of the intersting
// functions of the median module.
-int main(int argc, char * argv[]) {
+int main(int argc, char* argv[]) {
// Call suggest buffer size to determine the size of the buffer
// we need to build the data structure.
size_t n = 0;
// Allocate the buffer of the required size. And then
// initialize it (median_init_buffer).
- assert(median_suggest_buffer_size(.001, TEST_SIZE , &n) == MEDIAN_OK);
- void * b = malloc(n);
+ assert(median_suggest_buffer_size(.001, TEST_SIZE, &n) == MEDIAN_OK);
+ void* b = malloc(n);
+ assert(b);
median_buffer_t buf;
assert(median_init_buffer(b, n, .001, TEST_SIZE, &buf) == MEDIAN_OK);
// Initialize the spare buffer.
assert(median_suggest_scratch_buffer_size(buf, &n) == MEDIAN_OK);
- void * s = malloc(n);
+ void* s = malloc(n);
+ assert(s);
median_scratch_buffer_t scratch;
- assert(median_init_scratch_buffer(s,n,buf,&scratch) == MEDIAN_OK);
+ assert(median_init_scratch_buffer(s, n, buf, &scratch) == MEDIAN_OK);
// Insert some data.
- for(median_data_t i = 1; i < TEST_SIZE; i++) { // 1G
- assert(median_insert_data(buf,i,scratch) == MEDIAN_OK);
+ for (median_data_t i = 1; i < TEST_SIZE; i++) { // 1G
+ assert(median_insert_data(buf, i, scratch) == MEDIAN_OK);
}
// Dump the buffer.
@@ -36,15 +38,24 @@
median_data_t median;
assert(median_output_median(buf, &median, scratch) == MEDIAN_OK);
-
fprintf(stderr, "And the median is %x\n", (unsigned)median);
+ size_t obs = 0;
+ assert(median_suggest_json_string_size(buf, &obs) == MEDIAN_OK);
+ void* ob = malloc(obs);
+ assert(ob);
+ median_json_string_t mob;
+ assert(median_init_json_string(ob, obs, buf, &mob) == MEDIAN_OK);
+
// Free up the resources.
assert(median_deinit_buffer(buf) == MEDIAN_OK);
+ assert(median_deinit_scratch_buffer(scratch) == MEDIAN_OK);
+ assert(median_deinit_json_string(mob) == MEDIAN_OK);
// Free up the malloc'd space.
free(b);
free(s);
+ free(ob);
// All done, return 0
return 0;