Implementation of a simple Protobuf in C.
This commit is contained in:
parent
c225714d33
commit
9b81ab4c48
15 changed files with 876 additions and 0 deletions
83
.cproject
Normal file
83
.cproject
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||||
|
<cconfiguration id="cdt.managedbuild.toolchain.gnu.macosx.base.224877191">
|
||||||
|
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.macosx.base.224877191" moduleId="org.eclipse.cdt.core.settings" name="Default">
|
||||||
|
<externalSettings/>
|
||||||
|
<extensions>
|
||||||
|
<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
</extensions>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||||
|
<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.macosx.base.224877191" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
|
||||||
|
<folderInfo id="cdt.managedbuild.toolchain.gnu.macosx.base.224877191.1034317576" name="/" resourcePath="">
|
||||||
|
<toolChain id="cdt.managedbuild.toolchain.gnu.macosx.base.1907187732" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.base">
|
||||||
|
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.MachO64" id="cdt.managedbuild.target.gnu.platform.macosx.base.1750176153" name="Debug Platform" osList="macosx" superClass="cdt.managedbuild.target.gnu.platform.macosx.base"/>
|
||||||
|
<builder id="cdt.managedbuild.target.gnu.builder.macosx.base.961877285" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.macosx.base"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.base.964721044" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.base">
|
||||||
|
<inputType id="cdt.managedbuild.tool.macosx.c.linker.input.1137972523" superClass="cdt.managedbuild.tool.macosx.c.linker.input">
|
||||||
|
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||||
|
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||||
|
</inputType>
|
||||||
|
</tool>
|
||||||
|
<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.base.728950887" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.base"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.assembler.macosx.base.204018620" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.base">
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1200540724" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||||
|
</tool>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.345698190" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.base.1769902649" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.base"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.c.compiler.macosx.base.593165879" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.base">
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1995695314" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||||
|
</tool>
|
||||||
|
</toolChain>
|
||||||
|
</folderInfo>
|
||||||
|
</configuration>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||||
|
</cconfiguration>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||||
|
<project id="protobuf-c.null.682563435" name="protobuf-c"/>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||||
|
<storageModule moduleId="refreshScope"/>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
|
||||||
|
<buildTargets>
|
||||||
|
<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
|
<buildCommand>make</buildCommand>
|
||||||
|
<buildArguments/>
|
||||||
|
<buildTarget>all</buildTarget>
|
||||||
|
<stopOnError>true</stopOnError>
|
||||||
|
<useDefaultCommand>false</useDefaultCommand>
|
||||||
|
<runAllBuilders>true</runAllBuilders>
|
||||||
|
</target>
|
||||||
|
<target name="clean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
|
<buildCommand>make</buildCommand>
|
||||||
|
<buildArguments/>
|
||||||
|
<buildTarget>clean</buildTarget>
|
||||||
|
<stopOnError>true</stopOnError>
|
||||||
|
<useDefaultCommand>false</useDefaultCommand>
|
||||||
|
<runAllBuilders>true</runAllBuilders>
|
||||||
|
</target>
|
||||||
|
<target name="rebuild" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
|
<buildCommand>make</buildCommand>
|
||||||
|
<buildArguments/>
|
||||||
|
<buildTarget>rebuild</buildTarget>
|
||||||
|
<stopOnError>true</stopOnError>
|
||||||
|
<useDefaultCommand>false</useDefaultCommand>
|
||||||
|
<runAllBuilders>true</runAllBuilders>
|
||||||
|
</target>
|
||||||
|
</buildTargets>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="scannerConfiguration">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||||
|
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.224877191;cdt.managedbuild.toolchain.gnu.macosx.base.224877191.1034317576;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.593165879;cdt.managedbuild.tool.gnu.c.compiler.input.1995695314">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||||
|
</scannerConfigBuildInfo>
|
||||||
|
</storageModule>
|
||||||
|
</cproject>
|
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
*
|
||||||
|
|
||||||
|
!.gitignore
|
||||||
|
!Makefile
|
||||||
|
!**/
|
||||||
|
|
||||||
|
*.o
|
||||||
|
.settings/language.settings.xml
|
||||||
|
test_protobuf
|
26
.project
Normal file
26
.project
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>protobuf-c</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||||
|
<triggers>clean,full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||||
|
<triggers>full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||||
|
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||||
|
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
19
Makefile
Normal file
19
Makefile
Normal file
|
@ -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
|
49
Test1_protobuf.c
Normal file
49
Test1_protobuf.c
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
}
|
13
Test1_protobuf.h
Normal file
13
Test1_protobuf.h
Normal file
|
@ -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);
|
79
Test2_protobuf.c
Normal file
79
Test2_protobuf.c
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
12
Test2_protobuf.h
Normal file
12
Test2_protobuf.h
Normal file
|
@ -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);
|
107
protobuf.c
Normal file
107
protobuf.c
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/***
|
||||||
|
* Helper to derived protobuf objects
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
69
protobuf.h
Normal file
69
protobuf.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* protobuf.h
|
||||||
|
*
|
||||||
|
* Created on: Dec 7, 2016
|
||||||
|
* Author: JohnJones
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PROTOBUF_H__
|
||||||
|
#define __PROTOBUF_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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_ */
|
187
test_protobuf.h
Normal file
187
test_protobuf.h
Normal file
|
@ -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;
|
||||||
|
}
|
37
test_varint.h
Normal file
37
test_varint.h
Normal file
|
@ -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;
|
||||||
|
}
|
74
testit.c
Normal file
74
testit.c
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
80
varint.c
Normal file
80
varint.c
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#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;
|
||||||
|
}
|
32
varint.h
Normal file
32
varint.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/***
|
||||||
|
* handles varint
|
||||||
|
*/
|
||||||
|
#ifndef _VARINT_H_
|
||||||
|
#define _VARINT_H_
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
Loading…
Reference in a new issue