From 9b81ab4c487e0e18cb7f4704d1194ac2714ebc73 Mon Sep 17 00:00:00 2001 From: jmjatlanta Date: Thu, 8 Dec 2016 16:33:27 -0500 Subject: [PATCH] Implementation of a simple Protobuf in C. --- .cproject | 83 +++++++++++++++++++++ .gitignore | 9 +++ .project | 26 +++++++ Makefile | 19 +++++ Test1_protobuf.c | 49 +++++++++++++ Test1_protobuf.h | 13 ++++ Test2_protobuf.c | 79 ++++++++++++++++++++ Test2_protobuf.h | 12 +++ protobuf.c | 107 +++++++++++++++++++++++++++ protobuf.h | 69 +++++++++++++++++ test_protobuf.h | 187 +++++++++++++++++++++++++++++++++++++++++++++++ test_varint.h | 37 ++++++++++ testit.c | 74 +++++++++++++++++++ varint.c | 80 ++++++++++++++++++++ varint.h | 32 ++++++++ 15 files changed, 876 insertions(+) create mode 100644 .cproject create mode 100644 .gitignore create mode 100644 .project create mode 100644 Makefile create mode 100644 Test1_protobuf.c create mode 100644 Test1_protobuf.h create mode 100644 Test2_protobuf.c create mode 100644 Test2_protobuf.h create mode 100644 protobuf.c create mode 100644 protobuf.h create mode 100644 test_protobuf.h create mode 100644 test_varint.h create mode 100644 testit.c create mode 100644 varint.c create mode 100644 varint.h diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..40ef94d --- /dev/null +++ b/.cproject @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + + all + true + false + true + + + make + + clean + true + false + true + + + make + + rebuild + true + false + true + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46d242b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +* + +!.gitignore +!Makefile +!**/ + +*.o +.settings/language.settings.xml +test_protobuf diff --git a/.project b/.project new file mode 100644 index 0000000..3cd7414 --- /dev/null +++ b/.project @@ -0,0 +1,26 @@ + + + protobuf-c + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a65253d --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CFLAGS = -O0 -g3 -Wall +LFLAGS = +DEPS = protobuf.h +OBJS = testit.o protobuf.o varint.o Test1_protobuf.o Test2_protobuf.o + +%.o: %.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +test_protobuf: $(OBJS) + $(CC) -o $@ $^ $(LFLAGS) + +all: test_protobuf + +clean: + rm -f *.o + rm -f test_protobuf + +rebuild: clean all \ No newline at end of file diff --git a/Test1_protobuf.c b/Test1_protobuf.c new file mode 100644 index 0000000..ad083dc --- /dev/null +++ b/Test1_protobuf.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include "protobuf.h" +#include "varint.h" +#include "Test1_protobuf.h" + +enum WireType Test1_message_fields[] = { WIRETYPE_VARINT }; + +int Test1_new(struct Test1** test1) { + *test1 = (struct Test1*)malloc(sizeof(struct Test1)); + if (*test1 == NULL) + return 0; + (*test1)->a = 0; + return 1; +} + +int Test1_free(struct Test1* test1) { + free(test1); + return 1; +} + +int Test1_protobuf_encode(struct Test1* incoming, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written) { + protobuf_encode_varint(1, Test1_message_fields[0], incoming->a, buffer, max_buffer_length, bytes_written); + return 1; +} + +int Test1_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Test1** output) { + int pos = 0; + if (Test1_new(output) == 0) + return 0; + + while (pos < buffer_length) { // loop through buffer + size_t bytes_read = 0; + int field_no; + enum WireType field_type; + if (protobuf_decode_field_and_type(buffer, buffer_length, &field_no, &field_type, &bytes_read) == 0) { + Test1_free(*output); + return 0; + } + switch (field_no) { + case (1): + (*output)->a = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read); + pos += (unsigned int)bytes_read; + break; + } + } + return 1; +} diff --git a/Test1_protobuf.h b/Test1_protobuf.h new file mode 100644 index 0000000..f433638 --- /dev/null +++ b/Test1_protobuf.h @@ -0,0 +1,13 @@ +#pragma once + +#include "protobuf.h" + +struct Test1 { + int a; +}; + +int Test1_new(struct Test1** test1); +int Test1_free(struct Test1* test1); + +int Test1_protobuf_encode(struct Test1* incoming, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written); +int Test1_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Test1** output); diff --git a/Test2_protobuf.c b/Test2_protobuf.c new file mode 100644 index 0000000..af60dfd --- /dev/null +++ b/Test2_protobuf.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include "protobuf.h" +#include "varint.h" +#include "Test2_protobuf.h" + +enum WireType Test2_message_fields[] = { WIRETYPE_LENGTH_DELIMITED }; + +/** + * Allocate resources for this structure + * @param test2 the structure + * @returns true(1) on success + */ +int Test2_new(struct Test2** test2) { + *test2 = (struct Test2*)malloc(sizeof(struct Test2)); + if (*test2 == NULL) + return 0; + (*test2)->a = NULL; + + return 1; +} + +/** + * Free resources + * @param test2 the struct + * @returns true(1) on success + */ +int Test2_free(struct Test2* test2) { + if (test2->a != NULL) + free(test2->a); + free(test2); + return 1; +} + +/*** + * Encode struct into protobuf stream + * @param incoming the struct + * @param buffer the place to put the stream + * @param max_buffer_length the length of the buffer + * @param bytes_written the number of bytes used in encoding the stream + * @returns true(1) on success + */ +int Test2_protobuf_encode(struct Test2* incoming, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written) { + if (protobuf_encode_string(1, Test2_message_fields[0], incoming->a, buffer, max_buffer_length, bytes_written) == 0) + return 0; + return 1; +} + +/*** + * Decode a buffer into a struct + * @param buffer the incoming buffer + * @param buffer_length the length of the buffer + * @param output the resultant struct + * @returns true(1) on success + */ +int Test2_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Test2** output) { + int pos = 0; + if (Test2_new(output) == 0) + return 0; + + while (pos < buffer_length) { // loop through buffer + size_t bytes_read; + int field_no; + enum WireType field_type; + protobuf_decode_field_and_type(&buffer[pos], buffer_length - pos, &field_no, &field_type, &bytes_read); + pos += bytes_read; + bytes_read = 0; + switch(field_no) { + case 1: + protobuf_decode_string(&buffer[pos], buffer_length - pos, &((*output)->a), &bytes_read); + break; + } + pos += bytes_read; + + } + return 1; +} + diff --git a/Test2_protobuf.h b/Test2_protobuf.h new file mode 100644 index 0000000..b08bf3e --- /dev/null +++ b/Test2_protobuf.h @@ -0,0 +1,12 @@ +#pragma once + +#include "protobuf.h" + +struct Test2 { + char* a; +}; + +int Test2_protobuf_encode(struct Test2* incoming, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written); +int Test2_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Test2** output); +int Test2_free(struct Test2* test2); +int Test2_new(struct Test2** test2); diff --git a/protobuf.c b/protobuf.c new file mode 100644 index 0000000..5ad42ab --- /dev/null +++ b/protobuf.c @@ -0,0 +1,107 @@ +/*** + * Helper to derived protobuf objects + */ +#include +#include + +#include "varint.h" +#include "protobuf.h" + +/*** + * encode a string into the buffer + * @param field_number the field number + * @param field_type the field type + * @param incoming the string value + * @param buffer the pointer to where to place the encoded value + * @param max_buffer_length the buffer length remaining + * @param bytes_written the number of bytes written + * @returns true(1) on success + */ +int protobuf_encode_string(int field_number, enum WireType field_type, const char* incoming, unsigned char* buffer, + size_t max_buffer_length, size_t* bytes_written) { + // push the field number and wire type together + unsigned int field_no = field_number << 3; + unsigned long long field = field_no | field_type; + size_t bytes_processed; + // field type & number + varint_encode(field, buffer, max_buffer_length, &bytes_processed); + *bytes_written += bytes_processed; + // field size + varint_encode(strlen(incoming), &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_processed); + *bytes_written += bytes_processed; + // field value + memcpy(&buffer[*bytes_written], incoming, strlen(incoming)); + *bytes_written += strlen(incoming); + return 1; +} + +/*** + * encode a varint into the buffer + * @param field_number the field number + * @param field_type the field type + * @param incoming the value + * @param buffer the pointer to where to place the encoded value + * @param max_buffer_length the buffer length remaining + * @param bytes_written the number of bytes written + * @returns true(1) on success + */ +int protobuf_encode_varint(int field_number, enum WireType field_type, unsigned long long incoming, unsigned char* buffer, + size_t max_buffer_length, size_t* bytes_written) { + // push the field number and wire type together + unsigned int field_no = field_number << 3; + unsigned long long field = field_no | field_type; + size_t bytes_processed; + // field type & number + varint_encode(field, buffer, max_buffer_length, &bytes_processed); + *bytes_written += bytes_processed; + // field value + varint_encode(incoming, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_processed); + *bytes_written += bytes_processed; + return 1; + +} + +/** + * Pull a string from the protobuf buffer + * @param the buffer, positioned at the field size + * @param buffer_length the buffer length + * @param results the results (NOTE: will allocate memory) + * @param bytes_read the number of bytes read + * @returns true(1) on success + */ +int protobuf_decode_string(const unsigned char* buffer, size_t buffer_length, char** results, size_t* bytes_read) { + size_t pos = 0; + // grab the field size + int length = varint_decode(&buffer[pos], buffer_length - pos, bytes_read); + pos += *bytes_read; + + // allocate memory + *results = malloc(sizeof(char) * length); + if ((*results) == NULL) + return 0; + + // copy the string + memcpy((*results), &buffer[pos], length); + pos += length; + *bytes_read = pos; + + return 1; +} + +/*** + * retrieve field number and field type from current buffer at position 0 + * @param buffer the incoming buffer + * @param buffer_length the length of the buffer + * @param field_no the resultant field number + * @param field_type the field type + * @param bytes_read the number of bytes read from the buffer + */ +int protobuf_decode_field_and_type(const unsigned char* buffer, int buffer_length, int *field_no, enum WireType *field_type, size_t* bytes_read) { + unsigned long long field = varint_decode(buffer, buffer_length, bytes_read); + *field_no = field >> 3; + *field_type = field | *field_no; + return 1; +} + + + diff --git a/protobuf.h b/protobuf.h new file mode 100644 index 0000000..dee77e5 --- /dev/null +++ b/protobuf.h @@ -0,0 +1,69 @@ +/* + * protobuf.h + * + * Created on: Dec 7, 2016 + * Author: JohnJones + */ + +#ifndef __PROTOBUF_H__ +#define __PROTOBUF_H__ + +#include +#include + +enum WireType { + WIRETYPE_VARINT, + WIRETYPE_64BIT, + WIRETYPE_LENGTH_DELIMITED, + WIRETYPE_START_GROUP, + WIRETYPE_END_GROUP, + WIRETYPE_32BIT +}; + +/*** + * encode a string into the buffer + * @param field_number the field number + * @param incoming the string value + * @param buffer the pointer to where to place the encoded value + * @param max_buffer_length the buffer length remaining + * @param bytes_written the number of bytes written + * @returns true(1) on success + */ +int protobuf_encode_string(int field_number, enum WireType wire_type, const char* incoming, unsigned char* buffer, + size_t max_buffer_length, size_t* bytes_written); + +/** + * Pull a string from the protobuf buffer + * @param the buffer, positioned at the field size + * @param buffer_length the buffer length + * @param results the results (NOTE: will allocate memory) + * @param bytes_read the number of bytes read + * @returns true(1) on success + */ +int protobuf_decode_string(const unsigned char* buffer, size_t buffer_length, char** results, size_t* bytes_read); + +/*** + * encode a varint into the buffer + * @param field_number the field number + * @param field_type the field type + * @param incoming the value + * @param buffer the pointer to where to place the encoded value + * @param max_buffer_length the buffer length remaining + * @param bytes_written the number of bytes written + * @returns true(1) on success + */ +int protobuf_encode_varint(int field_number, enum WireType field_type, unsigned long long incoming, unsigned char* buffer, + size_t max_buffer_length, size_t* bytes_written); + +/*** + * retrieve field number and field type from current buffer at position 0 + * @param buffer the incoming buffer + * @param buffer_length the length of the buffer + * @param field_no the resultant field number + * @param field_type the field type + * @param bytes_read the number of bytes read from the buffer + */ +int protobuf_decode_field_and_type(const unsigned char* buffer, int buffer_length, int *field_no, enum WireType *field_type, size_t* bytes_read); + + +#endif /* PROTOBUF_H_ */ diff --git a/test_protobuf.h b/test_protobuf.h new file mode 100644 index 0000000..47bec42 --- /dev/null +++ b/test_protobuf.h @@ -0,0 +1,187 @@ + +/*** + * This... + * + * message Test1 { + * required int32 a = 1; + * } + * + * should generate Test1_protobuf.h and c that contains this... + * + * enum WireType Test1_message_fields[] = { WIRETYPE_VARINT } + * + * struct Test1 { + * int a; + * } + * + * int Test1_protobuf_encode(struct Test1* incoming, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written); + * int Test1_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Test1** output); + */ + +#include "Test1_protobuf.h" +#include "Test2_protobuf.h" + +int test_rightshift() { + struct Test1 test1; + test1.a = 0; + + size_t buffer_size = 256; + unsigned char buffer[buffer_size]; + memset(buffer, 0, 256); + + int retVal = 0; + size_t bytes_written = 0; + retVal = Test1_protobuf_encode(&test1, buffer, buffer_size, &bytes_written); + + if (retVal == 0) { + printf("Error encoding\n"); + return 0; + } + + if (bytes_written != 2) { + printf("Incorrect number of bytes written. Expected 2, but got %d\n", retVal); + return 0; + } + + unsigned int pos0 = buffer[0]; + unsigned int pos1 = buffer[1]; + + if (pos0 != 8) { + printf("Results at pos 0 not as expected\n"); + return 0; + } + + if (pos1 != 0) { + printf("Results at pos 1 not as expected\n"); + return 0; + } + + return 1; +} + +int test_write_simple() { + struct Test1 test1; + test1.a = 150; + + size_t buffer_size = 256; + unsigned char buffer[buffer_size]; + memset(buffer, 0, 256); + + int retVal = 0; + size_t bytes_written = 0; + retVal = Test1_protobuf_encode(&test1, buffer, buffer_size, &bytes_written); + + if (retVal == 0) { + printf("Error encoding\n"); + return 0; + } + + if (bytes_written != 3) { + printf("Incorrect number of bytes written. Expected 3, but got %d\n", retVal); + return 0; + } + + unsigned int pos0 = buffer[0]; + unsigned int pos1 = buffer[1]; + unsigned int pos2 = buffer[2]; + + if (pos0 != 8 || pos1 != 150 || pos2 != 1) { + printf("Results not as expected\n"); + return 0; + } + + return 1; +} + +int test_write_negative() { + struct Test1 test1; + test1.a = -150; + + size_t buffer_size = 256; + unsigned char buffer[buffer_size]; + memset(buffer, 0, 256); + + int retVal = 0; + size_t bytes_written = 0; + retVal = Test1_protobuf_encode(&test1, buffer, buffer_size, &bytes_written); + + if (retVal == 0) { + printf("Error encoding\n"); + return 0; + } + + if (bytes_written != 11) { + printf("Incorrect number of bytes written. Expected 11, but got %d\n", retVal); + return 0; + } + + unsigned int pos0 = buffer[0]; + unsigned int pos1 = buffer[1]; + unsigned int pos2 = buffer[2]; + unsigned int pos3 = buffer[3]; + unsigned int pos4 = buffer[4]; + unsigned int pos5 = buffer[5]; + unsigned int pos6 = buffer[6]; + unsigned int pos7 = buffer[7]; + unsigned int pos8 = buffer[8]; + unsigned int pos9 = buffer[9]; + unsigned int pos10 = buffer[10]; + + if (pos0 != 8 || pos1 != 234 || pos2 != 254 + || pos3 != 255 || pos4 != 255 || pos5 != 255 || pos6 != 255 + || pos7 != 255 || pos8 != 255 || pos9 != 255 || pos10 != 1 + ) { + printf("Results not as expected\n"); + return 0; + } + + return 1; +} + +int test_write_string() { + struct Test2 test2; + test2.a = "testing"; + + size_t buffer_size = 256; + unsigned char buffer[buffer_size]; + memset(buffer, 0, 256); + + int retVal = 0; + size_t bytes_written = 0; + + retVal = Test2_protobuf_encode(&test2, buffer, buffer_size, &bytes_written); + + if (retVal == 0) { + printf("Error encoding\n"); + return 0; + } + + if (bytes_written != 9) { + printf("Incorrect number of bytes written. Expected 9, but got %d\n", retVal); + return 0; + } + + unsigned char expected[] = { 10, 7, 't', 'e', 's', 't', 'i', 'n', 'g' }; + + int correct = 1; + for(int i = 0; i < 9; i++) { + if (expected[i] != buffer[i]) { + correct = 0; + printf("Incorrect value at position %d... Expected %02x but received %02x.\n", i, expected[i], buffer[i]); + } + } + if (!correct) + return 0; + + struct Test2* test_results; + Test2_protobuf_decode(buffer, bytes_written, &test_results); + if (strcmp(test2.a, test_results->a) != 0) { + printf("Strings do not match. %s vs %s", test2.a, test_results->a); + Test2_free(test_results); + return 0; + } + + Test2_free(test_results); + + return 1; +} diff --git a/test_varint.h b/test_varint.h new file mode 100644 index 0000000..42f2e53 --- /dev/null +++ b/test_varint.h @@ -0,0 +1,37 @@ +#include "varint.h" + +int test_varint() { + + unsigned long long ull = 150; + size_t buffer_size = 256; + unsigned char buffer[buffer_size]; + + memset(buffer, 0, 256); + + size_t results; + varint_encode(ull, buffer, buffer_size, &results); + + unsigned int pos0 = (unsigned int)buffer[0]; + unsigned int pos1 = (unsigned int)buffer[1]; + + if ( pos0 != 150) { + printf("Expected %d but got %d at position 0\n", 150, pos0); + return 0; + } + + if (pos1 != 1) { + printf("Expected %d but got %d at position 1\n", 2, pos1); + return 0; + } + + // now do the reverse + + unsigned long long newVal = varint_decode(buffer, (int)results, &results); + + if (newVal != ull) { + printf("Expected %llu but received %llu\n", ull, newVal); + return 0; + } + + return 1; +} diff --git a/testit.c b/testit.c new file mode 100644 index 0000000..a510f65 --- /dev/null +++ b/testit.c @@ -0,0 +1,74 @@ +#include +#include + +#include "test_protobuf.h" +#include "test_varint.h" + +int testit(const char* name, int (*func)(void)) { + printf("Testing %s...\n", name); + int retVal = func(); + if (retVal) + printf("%s success!\n", name); + else + printf("** Uh oh! %s failed.**\n", name); + return retVal == 0; +} + +const char* names[] = { + "test_write_simple", + "test_write_negative", + "test_write_string", + "test_rightshift", + "test_varint" +}; + +int (*funcs[])(void) = { + test_write_simple, + test_write_negative, + test_write_string, + test_rightshift, + test_varint +}; + +/** + * run 1 test or run all + */ +int main(int argc, char** argv) { + int counter = 0; + int tests_ran = 0; + char* test_wanted; + int only_one = 0; + if(argc > 1) { + only_one = 1; + if (argv[1][0] == '\'') { // some shells put quotes around arguments + argv[1][strlen(argv[1])-1] = 0; + test_wanted = &(argv[1][1]); + } + else + test_wanted = argv[1]; + } + int array_length = sizeof(funcs) / sizeof(funcs[0]); + for (int i = 0; i < array_length; i++) { + if (only_one && strcmp(names[i], test_wanted) == 0) { + tests_ran++; + counter += testit(names[i], funcs[i]); + } + else + if (!only_one) { + tests_ran++; + counter += testit(names[i], funcs[i]); + } + + } + + if (tests_ran == 0) + printf("***** No tests found *****\n"); + else { + if (counter > 0) { + printf("***** There were %d failed test(s) *****\n", counter); + } else { + printf("All %d tests passed\n", tests_ran); + } + } + return 1; +} diff --git a/varint.c b/varint.c new file mode 100644 index 0000000..3294515 --- /dev/null +++ b/varint.c @@ -0,0 +1,80 @@ +#include +#include "varint.h" + +static const char MSB = 0x80; +static const char MSBALL = ~0x7F; + +static const unsigned long long N1 = 128; // 2 ^ 7 +static const unsigned long long N2 = 16384; +static const unsigned long long N3 = 2097152; +static const unsigned long long N4 = 268435456; +static const unsigned long long N5 = 34359738368; +static const unsigned long long N6 = 4398046511104; +static const unsigned long long N7 = 562949953421312; +static const unsigned long long N8 = 72057594037927936; +static const unsigned long long N9 = 9223372036854775808U; + +int varint_encoding_length(unsigned long long n) { + return ( + n < N1 ? 1 + : n < N2 ? 2 + : n < N3 ? 3 + : n < N4 ? 4 + : n < N5 ? 5 + : n < N6 ? 6 + : n < N7 ? 7 + : n < N8 ? 8 + : n < N9 ? 9 + : 10 + ); +} + +/** + * Encode an unsigned long long into a varint + * @param n the number to encode + * @param buf where to put the results + * @param len the length of buf + * @param bytes the length written + * @returns a pointer to the buf + */ +unsigned char* varint_encode(const unsigned long long n, unsigned char* buf, int len, size_t* bytes) { + unsigned char* ptr = buf; + unsigned long long copy = n; + + while (copy & MSBALL) { + *(ptr++) = (copy & 0xFF) | MSB; + copy = copy >> 7; + assert((ptr - buf) < len); + } + *ptr = copy; + if (bytes != NULL) *bytes = ptr - buf + 1; + + return buf; +} + +/*** + * decode a varint + * @param buf the buffer that contains the varint + * @param len the length of the buffer + * @param bytes number of bytes processed + * @returns the value decoded + */ +unsigned long long varint_decode(const unsigned char* buf, int len, size_t* bytes) { + unsigned long long result = 0; + int bits = 0; + const unsigned char* ptr = buf; + unsigned long long ll; + while (*ptr & MSB) { + ll = *ptr; + result += ((ll & 0x7F) << bits); + ptr++; + bits += 7; + assert((ptr - buf) < len); + } + ll = *ptr; + result += ((ll & 0x7F) << bits); + + if (bytes != NULL) *bytes = ptr - buf + 1; + + return result; +} diff --git a/varint.h b/varint.h new file mode 100644 index 0000000..8d7d943 --- /dev/null +++ b/varint.h @@ -0,0 +1,32 @@ +/*** + * handles varint + */ +#ifndef _VARINT_H_ +#define _VARINT_H_ +#include + +/** + * Encode an unsigned long long into a varint + * @param n the number to encode + * @param buf where to put the results + * @param len the length of buf + * @param bytes the length written + * @returns a pointer to the buf + */ +unsigned char* varint_encode(const unsigned long long n, unsigned char* buf, int len, size_t* bytes); + +/*** + * decode a varint + * @param buf the buffer that contains the varint + * @param len the length of the buffer + * @param bytes number of bytes processed + * @returns the value decoded + */ +unsigned long long varint_decode(const unsigned char* buf, int len, size_t* bytes); + +/** + * + */ +int varint_encoding_length(unsigned long long n); + +#endif