From 1566d92426de9c4d66c039d52cd78a4e86e12161 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Tue, 24 Jan 2017 01:48:14 -0200 Subject: [PATCH 01/18] enhanced sequential reading --- README.md | 19 ++++++++++++++++++- netstring.c | 13 ++++++++++--- netstring.h | 2 +- testsuite.c | 39 ++++++++++++++++++++++++++------------- 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 4c8af4c..ce44146 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ All the code is in `netstring.c` and `netstring.h`, and these have no external d To parse a netstring, use `netstring_read()`. - int netstring_read(char *buffer, size_t buffer_length, + int netstring_read(char **buffer, size_t *buffer_length, char **netstring_start, size_t *netstring_length); Reads a netstring from a `buffer` of length `buffer_length`. Writes to @@ -39,6 +39,23 @@ values are: NETSTRING_ERROR_LEADING_ZERO Leading zeros are not allowed NETSTRING_ERROR_NO_LENGTH Length not given at start of netstring +Example: + + char *str, *base = buffer; + size_t len, size = bytes_read; + + while(netstring_read(&base, &size, &str, &len) == 0) { + do_something(str, len); + } + +We can replace the comma with a null terminator when reading: + + while(netstring_read(&base, &size, &str, &len) == 0) { + str[len] = 0; + puts(str); + str[len] = ','; /* and optionally restore it */ + } + If you're sending messages with more than 999999999 bytes -- about 2 GB -- then you probably should not be doing so in the form of a single netstring. This restriction is in place partially to protect from diff --git a/netstring.c b/netstring.c index b8e48f0..f8476c3 100644 --- a/netstring.c +++ b/netstring.c @@ -30,10 +30,12 @@ Example: if (netstring_read("3:foo,", 6, &str, &len) < 0) explode_and_die(); */ -int netstring_read(char *buffer, size_t buffer_length, +int netstring_read(char **pbuffer, size_t *pbuffer_length, char **netstring_start, size_t *netstring_length) { int i; size_t len = 0; + char *buffer = *pbuffer; + size_t buffer_length = *pbuffer_length; /* Write default values for outputs */ *netstring_start = NULL; *netstring_length = 0; @@ -64,9 +66,14 @@ int netstring_read(char *buffer, size_t buffer_length, /* Read the colon */ if (buffer[i++] != ':') return NETSTRING_ERROR_NO_COLON; - /* Test for the trailing comma, and set the return values */ + /* Test for the trailing comma */ if (buffer[i + len] != ',') return NETSTRING_ERROR_NO_COMMA; - *netstring_start = &buffer[i]; *netstring_length = len; + + /* Set the return values */ + *netstring_start = &buffer[i]; + *netstring_length = len; + *pbuffer = *netstring_start + len + 1; + *pbuffer_length = buffer_length - (i + len + 1); return 0; } diff --git a/netstring.h b/netstring.h index cb5564a..c8ddb9a 100644 --- a/netstring.h +++ b/netstring.h @@ -3,7 +3,7 @@ #include -int netstring_read(char *buffer, size_t buffer_length, +int netstring_read(char **pbuffer, size_t *pbuffer_length, char **netstring_start, size_t *netstring_length); size_t netstring_buffer_size(size_t data_length); diff --git a/testsuite.c b/testsuite.c index b377e86..e1888c4 100644 --- a/testsuite.c +++ b/testsuite.c @@ -21,62 +21,75 @@ char ex9[] = ":what's up"; /* No number */ void test_netstring_read(void) { - char *netstring; + char *base; + size_t base_len; + char *netstring; size_t netstring_len; int retval; /* ex1: hello world */ - retval = netstring_read(ex1, strlen(ex1), &netstring, &netstring_len); + base = ex1; base_len = strlen(ex1); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 12); assert(strncmp(netstring, "hello world!", 12) == 0); + assert(base == ex1 + strlen(ex1)); assert(base_len == 0); assert(retval == 0); /* ex2: three netstrings, concatenated. */ - retval = netstring_read(ex2, strlen(ex2), &netstring, &netstring_len); + base = ex2; base_len = strlen(ex2); + + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 3); assert(strncmp(netstring, "foo", 3) == 0); assert(retval == 0); - retval = netstring_read(netstring+netstring_len+1, 9, &netstring, &netstring_len); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 0); assert(retval == 0); - retval = netstring_read(netstring+netstring_len+1, 6, &netstring, &netstring_len); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 3); assert(strncmp(netstring, "bar", 3) == 0); - assert(retval == 0); + assert(retval == 0); assert(base_len == 0); /* ex3: no comma */ - retval = netstring_read(ex3, strlen(ex3), &netstring, &netstring_len); + base = ex3; base_len = strlen(ex3); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 0); assert(netstring == NULL); assert(retval == NETSTRING_ERROR_NO_COMMA); /* ex4: too short */ - retval = netstring_read(ex4, strlen(ex4), &netstring, &netstring_len); + base = ex4; base_len = strlen(ex4); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 0); assert(netstring == NULL); assert(retval == NETSTRING_ERROR_TOO_SHORT); /* ex5: leading zero */ - retval = netstring_read(ex5, strlen(ex5), &netstring, &netstring_len); + base = ex5; base_len = strlen(ex5); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 0); assert(netstring == NULL); assert(retval == NETSTRING_ERROR_LEADING_ZERO); /* ex6: too long */ - retval = netstring_read(ex6, strlen(ex6), &netstring, &netstring_len); + base = ex6; base_len = strlen(ex6); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 0); assert(netstring == NULL); assert(retval == NETSTRING_ERROR_TOO_LONG); /* ex7: no colon */ - retval = netstring_read(ex7, strlen(ex7), &netstring, &netstring_len); + base = ex7; base_len = strlen(ex7); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 0); assert(netstring == NULL); assert(retval == NETSTRING_ERROR_NO_COLON); /* ex8: no number or colon */ - retval = netstring_read(ex8, strlen(ex8), &netstring, &netstring_len); + base = ex8; base_len = strlen(ex8); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 0); assert(netstring == NULL); assert(retval == NETSTRING_ERROR_NO_LENGTH); /* ex9: no number */ - retval = netstring_read(ex9, strlen(ex9), &netstring, &netstring_len); + base = ex9; base_len = strlen(ex9); + retval = netstring_read(&base, &base_len, &netstring, &netstring_len); assert(netstring_len == 0); assert(netstring == NULL); assert(retval == NETSTRING_ERROR_NO_LENGTH); } From c393f4ecc7141fce5fcb3c323f9dab732bde50a7 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Tue, 24 Jan 2017 03:45:49 -0200 Subject: [PATCH 02/18] added netstring_add() --- netstring.c | 24 ++++++++++++++++++++++++ testsuite.c | 12 ++++++++++++ 2 files changed, 36 insertions(+) diff --git a/netstring.c b/netstring.c index f8476c3..e2a9c8e 100644 --- a/netstring.c +++ b/netstring.c @@ -109,3 +109,27 @@ size_t netstring_encode_new(char **netstring, char *data, size_t len) { return num_len + len + 2; } +int netstring_add(char **list, char *str) { + int size_prev=0, size_next; + char *ptr; + + if (list == 0 || str == 0) return 0; + + size_next = strlen(str) + 12; + + if (*list == 0) { + ptr = malloc(size_next); + if (ptr == 0) return 0; + *list = ptr; + } else { + size_prev = strlen(*list); + ptr = realloc(*list, size_prev + size_next); + if (ptr == 0) return 0; + *list = ptr; + ptr += size_prev; + } + + sprintf(ptr, "%d:%s,", strlen(str), str); + size_next = strlen(str); + return size_prev + size_next; +} \ No newline at end of file diff --git a/testsuite.c b/testsuite.c index e1888c4..e75437c 100644 --- a/testsuite.c +++ b/testsuite.c @@ -119,11 +119,23 @@ void test_netstring_encode_new(void) { free(ns); } +void test_netstring_add(void) { + char *list=0; + + netstring_add(&list, "first"); + netstring_add(&list, "second"); + netstring_add(&list, "third"); + assert(strcmp(list, "5:first,6:second,5:third,") == 0); + free(list); + +} + int main(void) { printf("Running test suite...\n"); test_netstring_read(); test_netstring_buffer_size(); test_netstring_encode_new(); + test_netstring_add(); printf("All tests passed!\n"); return 0; } From 9dd8e1b1c7e997d6d20ff378924d1d7b1a435bec Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Tue, 24 Jan 2017 03:57:01 -0200 Subject: [PATCH 03/18] added netstring_add on README --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ce44146..8f470fd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ All the code is in `netstring.c` and `netstring.h`, and these have no external d To parse a netstring, use `netstring_read()`. - int netstring_read(char **buffer, size_t *buffer_length, + int netstring_read(char **buffer_start, size_t *buffer_length, char **netstring_start, size_t *netstring_length); Reads a netstring from a `buffer` of length `buffer_length`. Writes to @@ -76,7 +76,19 @@ This code provides a convenience function for creating a new netstring: This allocates and creates a netstring containing the first `len` bytes of `data`. This must be manually freed by the client. If `len` is 0 then no data will be read from `data`, and it may be null. +Or with the `netstring_add` function: + + char *netstring=0; /* we must initialize it to zero */ + + netstring_add(&netstring, "first"); + netstring_add(&netstring, "second"); + netstring_add(&netstring, "third"); + + do_something(netstring); + free(netstring); + + Contributing ------------ -All this code is Public Domain. If you want to contribute, you can send bug reports, or fork the project on GitHub. Contributions are welcomed with open arms. \ No newline at end of file +All this code is Public Domain. If you want to contribute, you can send bug reports, or fork the project on GitHub. Contributions are welcomed with open arms. From 143cbee3c87e2a3b4e24db6f47d4d46cfb1d2b86 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Tue, 24 Jan 2017 04:27:47 -0200 Subject: [PATCH 04/18] added netstring_add_ex() --- README.md | 5 ++++- netstring.c | 10 +++++++--- netstring.h | 11 +++++++---- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8f470fd..916e728 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Example: do_something(str, len); } -We can replace the comma with a null terminator when reading: +We can replace the comma with a null terminator when reading (zero copy): while(netstring_read(&base, &size, &str, &len) == 0) { str[len] = 0; @@ -87,6 +87,9 @@ Or with the `netstring_add` function: do_something(netstring); free(netstring); +The extended version `netstring_add_ex` accepts a string length as the last argument: + + int netstring_add_ex(char **list, char *str, int len); Contributing ------------ diff --git a/netstring.c b/netstring.c index e2a9c8e..1e6b898 100644 --- a/netstring.c +++ b/netstring.c @@ -109,13 +109,13 @@ size_t netstring_encode_new(char **netstring, char *data, size_t len) { return num_len + len + 2; } -int netstring_add(char **list, char *str) { +int netstring_add_ex(char **list, char *str, int len) { int size_prev=0, size_next; char *ptr; if (list == 0 || str == 0) return 0; - size_next = strlen(str) + 12; + size_next = len + 12; if (*list == 0) { ptr = malloc(size_next); @@ -132,4 +132,8 @@ int netstring_add(char **list, char *str) { sprintf(ptr, "%d:%s,", strlen(str), str); size_next = strlen(str); return size_prev + size_next; -} \ No newline at end of file +} + +int netstring_add(char **list, char *str) { + return netstring_add_ex(list, str, strlen(str)); +} diff --git a/netstring.h b/netstring.h index c8ddb9a..b412718 100644 --- a/netstring.h +++ b/netstring.h @@ -3,13 +3,16 @@ #include -int netstring_read(char **pbuffer, size_t *pbuffer_length, - char **netstring_start, size_t *netstring_length); - -size_t netstring_buffer_size(size_t data_length); +int netstring_add(char **list, char *str); +int netstring_add_ex(char **list, char *str, int len); size_t netstring_encode_new(char **netstring, char *data, size_t len); +int netstring_read(char **buffer_start, size_t *buffer_length, + char **netstring_start, size_t *netstring_length); + +size_t netstring_buffer_size(size_t data_length); + /* Errors that can occur during netstring parsing */ #define NETSTRING_ERROR_TOO_LONG -1 #define NETSTRING_ERROR_NO_COLON -2 From 81fdbcb3fd6a86fd97467cd84bac3efcc7b7ba22 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Tue, 24 Jan 2017 21:18:46 -0200 Subject: [PATCH 05/18] added message framing example on README --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 916e728..b9424ef 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,35 @@ The extended version `netstring_add_ex` accepts a string length as the last argu int netstring_add_ex(char **list, char *str, int len); +Message Framing on stream-based connections (sockets, pipes...) +--------------------------------------------------------------- + +On stream-based connections the messages can arrive coalesced or fragmented. + +Here is an example of reading those messages using netstring for message framing: + + char buffer[1024], *buffer_base, *str; + int bytes_read, buffer_used = 0, len; + + while(1) { + /* read data from socket */ + bytes_read = recv(sock, &buffer[buffer_used], sizeof(buffer) - buffer_used); + if (bytes_read < 0) break; if (bytes_read == 0) continue; + buffer_used += bytes_read; + + /* parse the strings from the read buffer */ + buffer_base = buffer; + while(netstring_read(&buffer_base, &buffer_used, &str, &len) == 0) { + do_something(str, len); + } + + /* if there are remaining bytes, move to the beggining of buffer */ + if (buffer_base > buffer && buffer_used > 0) + memmove(buffer, buffer_base, buffer_used); + } + +Note: this example is lacking error checking from netstring_read function and it does not allocate memory for bigger messages. + Contributing ------------ From decc49e3f88fefcad0aeb5704f12b73b08c36456 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 25 Jan 2017 02:21:15 +0000 Subject: [PATCH 06/18] replaced int for size_t --- netstring.c | 13 +++++++------ netstring.h | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/netstring.c b/netstring.c index 1e6b898..d6e708c 100644 --- a/netstring.c +++ b/netstring.c @@ -31,7 +31,7 @@ if (netstring_read("3:foo,", 6, &str, &len) < 0) explode_and_die(); */ int netstring_read(char **pbuffer, size_t *pbuffer_length, - char **netstring_start, size_t *netstring_length) { + char **netstring_start, size_t *netstring_length) { int i; size_t len = 0; char *buffer = *pbuffer; @@ -108,9 +108,10 @@ size_t netstring_encode_new(char **netstring, char *data, size_t len) { *netstring = ns; return num_len + len + 2; } - -int netstring_add_ex(char **list, char *str, int len) { - int size_prev=0, size_next; + +/* returns the netrstring size not including the null terminator */ +size_t netstring_add_ex(char **list, char *str, size_t len) { + size_t size_prev=0, size_next; char *ptr; if (list == 0 || str == 0) return 0; @@ -130,10 +131,10 @@ int netstring_add_ex(char **list, char *str, int len) { } sprintf(ptr, "%d:%s,", strlen(str), str); - size_next = strlen(str); + size_next = strlen(ptr); return size_prev + size_next; } -int netstring_add(char **list, char *str) { +size_t netstring_add(char **list, char *str) { return netstring_add_ex(list, str, strlen(str)); } diff --git a/netstring.h b/netstring.h index b412718..3b468aa 100644 --- a/netstring.h +++ b/netstring.h @@ -3,8 +3,8 @@ #include -int netstring_add(char **list, char *str); -int netstring_add_ex(char **list, char *str, int len); +size_t netstring_add(char **netstring, char *data); +size_t netstring_add_ex(char **netstring, char *data, size_t len); size_t netstring_encode_new(char **netstring, char *data, size_t len); From c9f827fa77bab1f7038be49c4c340cdb61037957 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 25 Jan 2017 03:57:20 +0000 Subject: [PATCH 07/18] replaced log10 by numdigits --- makefile | 1 - netstring.c | 43 +++++++++++++++++++++++++++++++------------ testsuite.c | 25 ++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/makefile b/makefile index 180c501..f71d417 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,4 @@ CFLAGS = -O2 -Wall -LDFLAGS= -lm .PHONY: test clean diff --git a/netstring.c b/netstring.c index d6e708c..7f152d8 100644 --- a/netstring.c +++ b/netstring.c @@ -4,7 +4,6 @@ #include #include #include -#include #include "netstring.h" /* Reads a netstring from a `buffer` of length `buffer_length`. Writes @@ -78,11 +77,20 @@ int netstring_read(char **pbuffer, size_t *pbuffer_length, return 0; } +/* count the number of digits (base 10) in a positive integer */ +int numdigits(size_t len) { + int n = 1; + if ( len >= 100000000 ) { n += 8; len /= 100000000; } + if ( len >= 10000 ) { n += 4; len /= 10000; } + if ( len >= 100 ) { n += 2; len /= 100; } + if ( len >= 10 ) { n += 1; } + return n; +} + /* Return the length, in ASCII characters, of a netstring containing `data_length` bytes. */ size_t netstring_buffer_size(size_t data_length) { - if (data_length == 0) return 3; - return (size_t)ceil(log10((double)data_length + 1)) + data_length + 2; + return (size_t)numdigits(data_length) + data_length + 2; } /* Allocate and create a netstring containing the first `len` bytes of @@ -98,7 +106,7 @@ size_t netstring_encode_new(char **netstring, char *data, size_t len) { ns[1] = ':'; ns[2] = ','; } else { - num_len = (size_t)ceil(log10((double)len + 1)); + num_len = numdigits(len); ns = malloc(num_len + len + 2); sprintf(ns, "%lu:", (unsigned long)len); memcpy(ns + num_len + 1, data, len); @@ -109,29 +117,40 @@ size_t netstring_encode_new(char **netstring, char *data, size_t len) { return num_len + len + 2; } -/* returns the netrstring size not including the null terminator */ +/* Allocate and create a netstring containing the first `len` bytes of + `data`. This must be manually freed by the client. If `len` is 0 + then no data will be read from `data`, and it may be NULL. + Returns the netstring size not including the null terminator */ size_t netstring_add_ex(char **list, char *str, size_t len) { - size_t size_prev=0, size_next; + size_t num_len, size_prev=0, size_next; char *ptr; - if (list == 0 || str == 0) return 0; + if (list == 0 || (len > 0 && str == 0)) return 0; - size_next = len + 12; + num_len = numdigits(len); + size_next = num_len + len + 2; if (*list == 0) { - ptr = malloc(size_next); + ptr = malloc(size_next + 1); if (ptr == 0) return 0; *list = ptr; } else { size_prev = strlen(*list); - ptr = realloc(*list, size_prev + size_next); + ptr = realloc(*list, size_prev + size_next + 1); if (ptr == 0) return 0; *list = ptr; ptr += size_prev; } - sprintf(ptr, "%d:%s,", strlen(str), str); - size_next = strlen(ptr); + if (len == 0) { + strcpy(ptr, "0:,"); + } else { + sprintf(ptr, "%lu:", (unsigned long)len); + ptr += num_len + 1; + memcpy(ptr, str, len); + ptr += len; *ptr = ','; + ptr++; *ptr = 0; + } return size_prev + size_next; } diff --git a/testsuite.c b/testsuite.c index e75437c..7b4109f 100644 --- a/testsuite.c +++ b/testsuite.c @@ -119,13 +119,35 @@ void test_netstring_encode_new(void) { free(ns); } +void test_netstring_add_ex(void) { + char *ns=0; size_t bytes; + + bytes = netstring_add_ex(&ns, "foo", 3); + assert(ns != NULL); assert(strncmp(ns, "3:foo,", 6) == 0); assert(bytes == 6); + free(ns); ns = 0; + + bytes = netstring_add_ex(&ns, NULL, 0); + assert(ns != NULL); assert(strncmp(ns, "0:,", 3) == 0); assert(bytes == 3); + free(ns); ns = 0; + + bytes = netstring_add_ex(&ns, "hello world!", 12); assert(bytes == 16); + assert(ns != NULL); assert(strncmp(ns, "12:hello world!,", 16) == 0); + free(ns); ns = 0; + + bytes = netstring_add_ex(&ns, "hello world!", 5); assert(bytes == 8); + puts(ns); + assert(ns != NULL); assert(strncmp(ns, "5:hello,", 8) == 0); + free(ns); ns = 0; +} + void test_netstring_add(void) { char *list=0; netstring_add(&list, "first"); netstring_add(&list, "second"); netstring_add(&list, "third"); - assert(strcmp(list, "5:first,6:second,5:third,") == 0); + netstring_add(&list, ""); + assert(strcmp(list, "5:first,6:second,5:third,0:,") == 0); free(list); } @@ -135,6 +157,7 @@ int main(void) { test_netstring_read(); test_netstring_buffer_size(); test_netstring_encode_new(); + test_netstring_add_ex(); test_netstring_add(); printf("All tests passed!\n"); return 0; From 28fb7f091593708046bca4f4092653e9e5ba27a4 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 25 Jan 2017 06:16:12 +0000 Subject: [PATCH 08/18] update README on creating netstrings --- README.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b9424ef..66ba897 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Basic API All the code is in `netstring.c` and `netstring.h`, and these have no external dependencies. To use them, just include them in your application. Include `netstring.h` and link with the C code. -**Parsing netstrings** +### Parsing netstrings To parse a netstring, use `netstring_read()`. @@ -62,21 +62,17 @@ netstring. This restriction is in place partially to protect from malicious or erroneous input, and partly to be compatible with D. J. Bernstein's reference implementation. -**Creating netstrings** +### Creating netstrings -To create a netstring, there are a few ways to do it. You could do something really simple like this example from [the spec](http://cr.yp.to/proto/netstrings.txt): +You can create your netstrings manually like in this example: - if (printf("%lu:", len) < 0) barf(); - if (fwrite(buf, 1, len, stdout) < len) barf(); - if (putchar(',') < 0) barf(); + sprintf(buf, "%lu:%s,", strlen(str), str); -This code provides a convenience function for creating a new netstring: +This code provides a convenience function for creating netstrings: - size_t netstring_encode_new(char **netstring, char *data, size_t len); + size_t netstring_add(char **netstring, char *data); -This allocates and creates a netstring containing the first `len` bytes of `data`. This must be manually freed by the client. If `len` is 0 then no data will be read from `data`, and it may be null. - -Or with the `netstring_add` function: +Here is how to use it: char *netstring=0; /* we must initialize it to zero */ @@ -85,11 +81,14 @@ Or with the `netstring_add` function: netstring_add(&netstring, "third"); do_something(netstring); - free(netstring); + + free(netstring); /* we must free after using it */ The extended version `netstring_add_ex` accepts a string length as the last argument: - int netstring_add_ex(char **list, char *str, int len); + size_t netstring_add_ex(char **netstring, char *data, size_t len); + +This allocates and creates a netstring containing the first `len` bytes of `data`. If `len` is 0 then no data will be read from `data`, and it may be null. Message Framing on stream-based connections (sockets, pipes...) --------------------------------------------------------------- From 81a58e5181805e2619b1ce60b1fd9d798d04226d Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 25 Jan 2017 06:25:34 +0000 Subject: [PATCH 09/18] README on natural order: first write then read --- README.md | 64 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 66ba897..8f19fa7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -Netstring parsing -================= +Netstring for C +=============== A [netstring](http://en.wikipedia.org/wiki/Netstring) is a way of encoding a sequence of bytes for transmission over a network, or for serialization. They're very easy to work with. They encode the data's length, and can be concatenated trivially. The format was [defined by D. J. Bernstein](http://cr.yp.to/proto/netstrings.txt) and is used in various software. Examples of netstrings: @@ -18,7 +18,37 @@ Basic API All the code is in `netstring.c` and `netstring.h`, and these have no external dependencies. To use them, just include them in your application. Include `netstring.h` and link with the C code. -### Parsing netstrings +Creating netstrings +------------------- + +You can create your netstrings manually like in this example: + + sprintf(buf, "%lu:%s,", strlen(str), str); + +This code provides a convenience function for creating netstrings: + + size_t netstring_add(char **netstring, char *data); + +Here is how to use it: + + char *netstring=0; /* we must initialize it to zero */ + + netstring_add(&netstring, "first"); + netstring_add(&netstring, "second"); + netstring_add(&netstring, "third"); + + do_something(netstring); + + free(netstring); /* we must free after using it */ + +The extended version `netstring_add_ex` accepts a string length as the last argument: + + size_t netstring_add_ex(char **netstring, char *data, size_t len); + +This allocates and creates a netstring containing the first `len` bytes of `data`. If `len` is 0 then no data will be read from `data`, and it may be null. + +Parsing netstrings +------------------ To parse a netstring, use `netstring_read()`. @@ -62,34 +92,6 @@ netstring. This restriction is in place partially to protect from malicious or erroneous input, and partly to be compatible with D. J. Bernstein's reference implementation. -### Creating netstrings - -You can create your netstrings manually like in this example: - - sprintf(buf, "%lu:%s,", strlen(str), str); - -This code provides a convenience function for creating netstrings: - - size_t netstring_add(char **netstring, char *data); - -Here is how to use it: - - char *netstring=0; /* we must initialize it to zero */ - - netstring_add(&netstring, "first"); - netstring_add(&netstring, "second"); - netstring_add(&netstring, "third"); - - do_something(netstring); - - free(netstring); /* we must free after using it */ - -The extended version `netstring_add_ex` accepts a string length as the last argument: - - size_t netstring_add_ex(char **netstring, char *data, size_t len); - -This allocates and creates a netstring containing the first `len` bytes of `data`. If `len` is 0 then no data will be read from `data`, and it may be null. - Message Framing on stream-based connections (sockets, pipes...) --------------------------------------------------------------- From 9ead2b87ff4fde0b13ce334a4cee5ac1512f6a73 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 25 Jan 2017 04:40:51 +0000 Subject: [PATCH 10/18] removed netstring_encode_new() --- netstring.c | 46 +++++++++++----------------------------------- netstring.h | 2 -- testsuite.c | 18 ------------------ 3 files changed, 11 insertions(+), 55 deletions(-) diff --git a/netstring.c b/netstring.c index 7f152d8..0714f7d 100644 --- a/netstring.c +++ b/netstring.c @@ -27,7 +27,7 @@ D. J. Bernstein's reference implementation. Example: - if (netstring_read("3:foo,", 6, &str, &len) < 0) explode_and_die(); + if (netstring_read(&buf, &buflen, &str, &len) < 0) failed(); */ int netstring_read(char **pbuffer, size_t *pbuffer_length, char **netstring_start, size_t *netstring_length) { @@ -93,52 +93,28 @@ size_t netstring_buffer_size(size_t data_length) { return (size_t)numdigits(data_length) + data_length + 2; } -/* Allocate and create a netstring containing the first `len` bytes of - `data`. This must be manually freed by the client. If `len` is 0 - then no data will be read from `data`, and it may be NULL. */ -size_t netstring_encode_new(char **netstring, char *data, size_t len) { - char *ns; - size_t num_len = 1; - - if (len == 0) { - ns = malloc(3); - ns[0] = '0'; - ns[1] = ':'; - ns[2] = ','; - } else { - num_len = numdigits(len); - ns = malloc(num_len + len + 2); - sprintf(ns, "%lu:", (unsigned long)len); - memcpy(ns + num_len + 1, data, len); - ns[num_len + len + 1] = ','; - } - - *netstring = ns; - return num_len + len + 2; -} - /* Allocate and create a netstring containing the first `len` bytes of `data`. This must be manually freed by the client. If `len` is 0 then no data will be read from `data`, and it may be NULL. Returns the netstring size not including the null terminator */ -size_t netstring_add_ex(char **list, char *str, size_t len) { +size_t netstring_add_ex(char **netstring, char *data, size_t len) { size_t num_len, size_prev=0, size_next; char *ptr; - if (list == 0 || (len > 0 && str == 0)) return 0; + if (netstring == 0 || (len > 0 && data == 0)) return 0; num_len = numdigits(len); size_next = num_len + len + 2; - if (*list == 0) { + if (*netstring == 0) { ptr = malloc(size_next + 1); if (ptr == 0) return 0; - *list = ptr; + *netstring = ptr; } else { - size_prev = strlen(*list); - ptr = realloc(*list, size_prev + size_next + 1); + size_prev = strlen(*netstring); + ptr = realloc(*netstring, size_prev + size_next + 1); if (ptr == 0) return 0; - *list = ptr; + *netstring = ptr; ptr += size_prev; } @@ -147,13 +123,13 @@ size_t netstring_add_ex(char **list, char *str, size_t len) { } else { sprintf(ptr, "%lu:", (unsigned long)len); ptr += num_len + 1; - memcpy(ptr, str, len); + memcpy(ptr, data, len); ptr += len; *ptr = ','; ptr++; *ptr = 0; } return size_prev + size_next; } -size_t netstring_add(char **list, char *str) { - return netstring_add_ex(list, str, strlen(str)); +size_t netstring_add(char **netstring, char *data) { + return netstring_add_ex(netstring, data, strlen(data)); } diff --git a/netstring.h b/netstring.h index 3b468aa..567ce0f 100644 --- a/netstring.h +++ b/netstring.h @@ -6,8 +6,6 @@ size_t netstring_add(char **netstring, char *data); size_t netstring_add_ex(char **netstring, char *data, size_t len); -size_t netstring_encode_new(char **netstring, char *data, size_t len); - int netstring_read(char **buffer_start, size_t *buffer_length, char **netstring_start, size_t *netstring_length); diff --git a/testsuite.c b/testsuite.c index 7b4109f..ffdcce5 100644 --- a/testsuite.c +++ b/testsuite.c @@ -103,22 +103,6 @@ void test_netstring_buffer_size(void) { assert(netstring_buffer_size(12345) == 12345 + 5 + 2); } -void test_netstring_encode_new(void) { - char *ns; size_t bytes; - - bytes = netstring_encode_new(&ns, "foo", 3); - assert(ns != NULL); assert(strncmp(ns, "3:foo,", 6) == 0); assert(bytes == 6); - free(ns); - - bytes = netstring_encode_new(&ns, NULL, 0); - assert(ns != NULL); assert(strncmp(ns, "0:,", 3) == 0); assert(bytes == 3); - free(ns); - - bytes = netstring_encode_new(&ns, "hello world!", 12); assert(bytes == 16); - assert(ns != NULL); assert(strncmp(ns, "12:hello world!,", 16) == 0); - free(ns); -} - void test_netstring_add_ex(void) { char *ns=0; size_t bytes; @@ -135,7 +119,6 @@ void test_netstring_add_ex(void) { free(ns); ns = 0; bytes = netstring_add_ex(&ns, "hello world!", 5); assert(bytes == 8); - puts(ns); assert(ns != NULL); assert(strncmp(ns, "5:hello,", 8) == 0); free(ns); ns = 0; } @@ -156,7 +139,6 @@ int main(void) { printf("Running test suite...\n"); test_netstring_read(); test_netstring_buffer_size(); - test_netstring_encode_new(); test_netstring_add_ex(); test_netstring_add(); printf("All tests passed!\n"); From 2c38949e68ddbff586976b0200867817890d6696 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 25 Jan 2017 22:38:36 +0000 Subject: [PATCH 11/18] update README on reading netstrings --- README.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 8f19fa7..10a6ff0 100644 --- a/README.md +++ b/README.md @@ -50,17 +50,23 @@ This allocates and creates a netstring containing the first `len` bytes of `data Parsing netstrings ------------------ -To parse a netstring, use `netstring_read()`. +To parse a netstring use `netstring_read()`: int netstring_read(char **buffer_start, size_t *buffer_length, char **netstring_start, size_t *netstring_length); -Reads a netstring from a `buffer` of length `buffer_length`. Writes to -`netstring_start` a pointer to the beginning of the string in the -buffer, and to `netstring_length` the length of the string. Does not -allocate any memory. If it reads successfully, then it returns 0. If -there is an error, then the return value will be negative. The error -values are: +It reads a netstring from `buffer_start` of initial `buffer_length` and writes +to `netstring_start` a pointer to the beginning of the string in the +buffer and to `netstring_length` the length of the string. It also updates +the `buffer_start` to the start of the next netstring item and `buffer_length` +to the number of remaining bytes not processed in the buffer. + +It does not allocate any memory. + +### Return Value + +If it reads successfully then it returns 0. If there is an error then the +return value will be negative. The error values are: NETSTRING_ERROR_TOO_LONG More than 999999999 bytes in a field NETSTRING_ERROR_NO_COLON No colon was found after the number @@ -69,7 +75,7 @@ values are: NETSTRING_ERROR_LEADING_ZERO Leading zeros are not allowed NETSTRING_ERROR_NO_LENGTH Length not given at start of netstring -Example: +Usage Example: char *str, *base = buffer; size_t len, size = bytes_read; @@ -86,8 +92,8 @@ We can replace the comma with a null terminator when reading (zero copy): str[len] = ','; /* and optionally restore it */ } -If you're sending messages with more than 999999999 bytes -- about 2 -GB -- then you probably should not be doing so in the form of a single +If you're sending messages with more than 999999999 bytes (about 2 +GB) then you probably should not be doing so in the form of a single netstring. This restriction is in place partially to protect from malicious or erroneous input, and partly to be compatible with D. J. Bernstein's reference implementation. From 46023e6fe1d78223a028ed0e7773c43043d539da Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Tue, 31 Jan 2017 03:34:17 +0000 Subject: [PATCH 12/18] added .travis.yml --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d2c5772 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: c + +script: + - make + - make test From f9cfacd54987bc86b795f6cc05abdd774009cd14 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Tue, 31 Jan 2017 03:40:52 -0200 Subject: [PATCH 13/18] added travis badge on README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 10a6ff0..1c786b8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ Netstring for C =============== +[![Build Status](https://travis-ci.org/liteserver/netstring-c.svg?branch=master)](https://travis-ci.org/liteserver/netstring-c) A [netstring](http://en.wikipedia.org/wiki/Netstring) is a way of encoding a sequence of bytes for transmission over a network, or for serialization. They're very easy to work with. They encode the data's length, and can be concatenated trivially. The format was [defined by D. J. Bernstein](http://cr.yp.to/proto/netstrings.txt) and is used in various software. Examples of netstrings: From 995bc45aea3e52ddec3d1739afff7d1418a4c582 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Tue, 31 Jan 2017 03:57:59 -0200 Subject: [PATCH 14/18] code with syntax highlighting on README --- README.md | 100 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 1c786b8..f281eeb 100644 --- a/README.md +++ b/README.md @@ -24,27 +24,35 @@ Creating netstrings You can create your netstrings manually like in this example: - sprintf(buf, "%lu:%s,", strlen(str), str); +```C +sprintf(buf, "%lu:%s,", strlen(str), str); +``` This code provides a convenience function for creating netstrings: - size_t netstring_add(char **netstring, char *data); +```C +size_t netstring_add(char **netstring, char *data); +``` Here is how to use it: - char *netstring=0; /* we must initialize it to zero */ +```C +char *netstring=0; /* we must initialize it to zero */ - netstring_add(&netstring, "first"); - netstring_add(&netstring, "second"); - netstring_add(&netstring, "third"); +netstring_add(&netstring, "first"); +netstring_add(&netstring, "second"); +netstring_add(&netstring, "third"); - do_something(netstring); - - free(netstring); /* we must free after using it */ +do_something(netstring); + +free(netstring); /* we must free after using it */ +``` The extended version `netstring_add_ex` accepts a string length as the last argument: - size_t netstring_add_ex(char **netstring, char *data, size_t len); +```C +size_t netstring_add_ex(char **netstring, char *data, size_t len); +``` This allocates and creates a netstring containing the first `len` bytes of `data`. If `len` is 0 then no data will be read from `data`, and it may be null. @@ -53,8 +61,10 @@ Parsing netstrings To parse a netstring use `netstring_read()`: - int netstring_read(char **buffer_start, size_t *buffer_length, - char **netstring_start, size_t *netstring_length); +```C +int netstring_read(char **buffer_start, size_t *buffer_length, + char **netstring_start, size_t *netstring_length); +``` It reads a netstring from `buffer_start` of initial `buffer_length` and writes to `netstring_start` a pointer to the beginning of the string in the @@ -78,20 +88,24 @@ return value will be negative. The error values are: Usage Example: - char *str, *base = buffer; - size_t len, size = bytes_read; +```C +char *str, *base = buffer; +size_t len, size = bytes_read; - while(netstring_read(&base, &size, &str, &len) == 0) { - do_something(str, len); - } +while(netstring_read(&base, &size, &str, &len) == 0) { + do_something(str, len); +} +``` We can replace the comma with a null terminator when reading (zero copy): - while(netstring_read(&base, &size, &str, &len) == 0) { - str[len] = 0; - puts(str); - str[len] = ','; /* and optionally restore it */ - } +```C +while(netstring_read(&base, &size, &str, &len) == 0) { + str[len] = 0; + puts(str); + str[len] = ','; /* and optionally restore it */ +} +``` If you're sending messages with more than 999999999 bytes (about 2 GB) then you probably should not be doing so in the form of a single @@ -105,27 +119,27 @@ Message Framing on stream-based connections (sockets, pipes...) On stream-based connections the messages can arrive coalesced or fragmented. Here is an example of reading those messages using netstring for message framing: - - char buffer[1024], *buffer_base, *str; - int bytes_read, buffer_used = 0, len; - - while(1) { - /* read data from socket */ - bytes_read = recv(sock, &buffer[buffer_used], sizeof(buffer) - buffer_used); - if (bytes_read < 0) break; if (bytes_read == 0) continue; - buffer_used += bytes_read; - - /* parse the strings from the read buffer */ - buffer_base = buffer; - while(netstring_read(&buffer_base, &buffer_used, &str, &len) == 0) { - do_something(str, len); - } - - /* if there are remaining bytes, move to the beggining of buffer */ - if (buffer_base > buffer && buffer_used > 0) - memmove(buffer, buffer_base, buffer_used); - } - +```C +char buffer[1024], *buffer_base, *str; +int bytes_read, buffer_used = 0, len; + +while(1) { + /* read data from socket */ + bytes_read = recv(sock, &buffer[buffer_used], sizeof(buffer) - buffer_used); + if (bytes_read < 0) break; if (bytes_read == 0) continue; + buffer_used += bytes_read; + + /* parse the strings from the read buffer */ + buffer_base = buffer; + while(netstring_read(&buffer_base, &buffer_used, &str, &len) == 0) { + do_something(str, len); + } + + /* if there are remaining bytes, move to the beggining of buffer */ + if (buffer_base > buffer && buffer_used > 0) + memmove(buffer, buffer_base, buffer_used); +} +``` Note: this example is lacking error checking from netstring_read function and it does not allocate memory for bigger messages. Contributing From b6414994fbae89ce9181f25596da6fb6287a48cf Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 28 Nov 2018 14:10:13 -0200 Subject: [PATCH 15/18] add list functions --- netstring.c | 29 +++++++++++++++++++++++++++++ netstring.h | 3 +++ testsuite.c | 23 +++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/netstring.c b/netstring.c index 0714f7d..b10b334 100644 --- a/netstring.c +++ b/netstring.c @@ -77,6 +77,35 @@ int netstring_read(char **pbuffer, size_t *pbuffer_length, return 0; } +/* Retrieves the size of the concatenated netstrings */ +int netstring_list_size(char *buffer, int size, size_t *ptotal) { + char *str, *base = buffer; + size_t len, remaining = size; + int rc; + + while( remaining>0 && (rc=netstring_read(&base, &remaining, &str, &len))==0 ){ + } + + if( rc==NETSTRING_ERROR_NO_LENGTH || rc==NETSTRING_ERROR_TOO_SHORT ) rc = 0; + *ptotal = size - remaining; + return rc; +} + +/* Retrieves the number of concatenated netstrings */ +int netstring_list_count(char *buffer, int size, int *pcount) { + char *str, *base = buffer; + size_t len, remaining = size; + int rc, count = 0; + + while( remaining>0 && (rc=netstring_read(&base, &remaining, &str, &len))==0 ){ + count++; + } + + if( rc==NETSTRING_ERROR_NO_LENGTH || rc==NETSTRING_ERROR_TOO_SHORT ) rc = 0; + *pcount = count; + return rc; +} + /* count the number of digits (base 10) in a positive integer */ int numdigits(size_t len) { int n = 1; diff --git a/netstring.h b/netstring.h index 567ce0f..de02aa7 100644 --- a/netstring.h +++ b/netstring.h @@ -11,6 +11,9 @@ int netstring_read(char **buffer_start, size_t *buffer_length, size_t netstring_buffer_size(size_t data_length); +int netstring_list_size(char *buffer, int size, size_t *ptotal); +int netstring_list_count(char *buffer, int size, int *pcount); + /* Errors that can occur during netstring parsing */ #define NETSTRING_ERROR_TOO_LONG -1 #define NETSTRING_ERROR_NO_COLON -2 diff --git a/testsuite.c b/testsuite.c index ffdcce5..083bfbd 100644 --- a/testsuite.c +++ b/testsuite.c @@ -9,6 +9,7 @@ /* Good examples */ char ex1[] = "12:hello world!,"; char ex2[] = "3:foo,0:,3:bar,"; +char exb[] = "3:foo,0:,3:bar, "; /* Bad examples */ char ex3[] = "12:hello world! "; /* No comma */ @@ -94,6 +95,26 @@ void test_netstring_read(void) { assert(retval == NETSTRING_ERROR_NO_LENGTH); } +void test_netstring_list_size(void) { + size_t size=0; + assert(netstring_list_size(ex1, strlen(ex1), &size) == 0); + assert(size == strlen(ex1)); + assert(netstring_list_size(ex2, strlen(ex2), &size) == 0); + assert(size == strlen(ex2)); + assert(netstring_list_size(exb, strlen(exb), &size) == 0); + assert(size == strlen(ex2)); +} + +void test_netstring_list_count(void) { + int count=0; + assert(netstring_list_count(ex1, strlen(ex1), &count) == 0); + assert(count == 1); + assert(netstring_list_count(ex2, strlen(ex2), &count) == 0); + assert(count == 3); + assert(netstring_list_count(exb, strlen(exb), &count) == 0); + assert(count == 3); +} + void test_netstring_buffer_size(void) { assert(netstring_buffer_size(0) == 3); assert(netstring_buffer_size(1) == 4); @@ -138,6 +159,8 @@ void test_netstring_add(void) { int main(void) { printf("Running test suite...\n"); test_netstring_read(); + test_netstring_list_size(); + test_netstring_list_count(); test_netstring_buffer_size(); test_netstring_add_ex(); test_netstring_add(); From 28ef86bccb2045c923889d10adad643854813ad8 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 28 Nov 2018 14:21:28 -0200 Subject: [PATCH 16/18] Update README.md --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index f281eeb..442f993 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,25 @@ while(1) { ``` Note: this example is lacking error checking from netstring_read function and it does not allocate memory for bigger messages. +Additional Functions +-------------------- + +### netstring_list_size + +Retrieves the size of the netstring list (concatenated netstrings) discarding trailing spaces. + +```C +int netstring_list_size(char *buffer, size_t size, size_t *ptotal); +``` + +### netstring_list_count + +Retrieves the number of items in a netstring list. + +```C +int netstring_list_count(char *buffer, size_t size, int *pcount); +``` + Contributing ------------ From a12266d6aa9812116185faacf6cdc2b2e542da68 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Wed, 28 Nov 2018 14:24:10 -0200 Subject: [PATCH 17/18] use size_t on new functions --- netstring.c | 4 ++-- netstring.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/netstring.c b/netstring.c index b10b334..fc7892c 100644 --- a/netstring.c +++ b/netstring.c @@ -78,7 +78,7 @@ int netstring_read(char **pbuffer, size_t *pbuffer_length, } /* Retrieves the size of the concatenated netstrings */ -int netstring_list_size(char *buffer, int size, size_t *ptotal) { +int netstring_list_size(char *buffer, size_t size, size_t *ptotal) { char *str, *base = buffer; size_t len, remaining = size; int rc; @@ -92,7 +92,7 @@ int netstring_list_size(char *buffer, int size, size_t *ptotal) { } /* Retrieves the number of concatenated netstrings */ -int netstring_list_count(char *buffer, int size, int *pcount) { +int netstring_list_count(char *buffer, size_t size, int *pcount) { char *str, *base = buffer; size_t len, remaining = size; int rc, count = 0; diff --git a/netstring.h b/netstring.h index de02aa7..f31343d 100644 --- a/netstring.h +++ b/netstring.h @@ -11,8 +11,8 @@ int netstring_read(char **buffer_start, size_t *buffer_length, size_t netstring_buffer_size(size_t data_length); -int netstring_list_size(char *buffer, int size, size_t *ptotal); -int netstring_list_count(char *buffer, int size, int *pcount); +int netstring_list_size(char *buffer, size_t size, size_t *ptotal); +int netstring_list_count(char *buffer, size_t size, int *pcount); /* Errors that can occur during netstring parsing */ #define NETSTRING_ERROR_TOO_LONG -1 From 1d999db14e7f7c94c06864e8f285196af6faa107 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Thu, 8 Oct 2020 22:57:46 +1100 Subject: [PATCH 18/18] docs: fix simple typo, beggining -> beginning There is a small typo in README.md. Should read `beginning` rather than `beggining`. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 442f993..3f8e03b 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ while(1) { do_something(str, len); } - /* if there are remaining bytes, move to the beggining of buffer */ + /* if there are remaining bytes, move to the beginning of buffer */ if (buffer_base > buffer && buffer_used > 0) memmove(buffer, buffer_base, buffer_used); }