New encode feature allows direct writing of byte string value (#137)

New features for UsefulBuf and for QCBOREncode allows direct writing to the output buffer. For QCBOREncode, this is the direct writing of the value of a byte string. This allows it to be written in chunks or be the output buffer of some function like symmetric encryption.


* fix grammer in security policy

* Half-way start a encoding feature to write byte string values into output

* Add documentation for OpenBstr

* UsefulBuf_Advance mostly working and testing

* OpenBytes() is mostly working and somewhat tested

* Finish up OpenBytes -- error handing, documentation...

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/test/UsefulBuf_Tests.c b/test/UsefulBuf_Tests.c
index 17ee051..3ab3557 100644
--- a/test/UsefulBuf_Tests.c
+++ b/test/UsefulBuf_Tests.c
@@ -1,6 +1,6 @@
 /*==============================================================================
  Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2022, Laurence Lundblade.
  Copyright (c) 2021, Arm Limited.
  All rights reserved.
 
@@ -815,4 +815,48 @@
 #endif /* USEFULBUF_DISABLE_ALL_FLOAT */
 
 
+const char *UBAdvanceTest(void)
+{
+   #define ADVANCE_TEST_SIZE 10
+   UsefulOutBuf_MakeOnStack(UOB, ADVANCE_TEST_SIZE);
 
+   UsefulBuf Place = UsefulOutBuf_GetOutPlace(&UOB);
+   if(Place.len != 10) {
+      return "GetOutPlace wrong size";
+   }
+
+   memset(Place.ptr, 'x', Place.len/2);
+
+   UsefulOutBuf_Advance(&UOB, Place.len/2);
+
+   UsefulOutBuf_AppendByte(&UOB, 'y');
+
+   Place = UsefulOutBuf_GetOutPlace(&UOB);
+   if(Place.len != ADVANCE_TEST_SIZE/2 -1 ) {
+      return "GetOutPlace wrong size 2";
+   }
+
+   memset(Place.ptr, 'z', Place.len);
+
+   UsefulOutBuf_Advance(&UOB, Place.len);
+
+   UsefulBufC O = UsefulOutBuf_OutUBuf(&UOB);
+
+   UsefulBuf_Compare(O, UsefulBuf_FROM_SZ_LITERAL("xxxxxyzzzz"));
+
+   Place = UsefulOutBuf_GetOutPlace(&UOB);
+   if(Place.len != 0 || Place.ptr != NULL) {
+      return "GetOutPlace not null";
+   }
+
+   if(UsefulOutBuf_GetError(&UOB)) {
+      return "GetOutPlace error set";
+   }
+
+   UsefulOutBuf_Advance(&UOB, 1);
+   if(!UsefulOutBuf_GetError(&UOB)) {
+      return "Advance off end didn't set error";
+   }
+
+   return NULL;
+}
diff --git a/test/UsefulBuf_Tests.h b/test/UsefulBuf_Tests.h
index 0d06206..235358e 100644
--- a/test/UsefulBuf_Tests.h
+++ b/test/UsefulBuf_Tests.h
@@ -50,4 +50,6 @@
 const char *  UBUTest_CopyUtil(void);
 #endif /* USEFULBUF_DISABLE_ALL_FLOAT */
 
+const char * UBAdvanceTest(void);
+
 #endif
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 70fc014..edb0861 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -1,6 +1,6 @@
 /*==============================================================================
  Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2022, Laurence Lundblade.
  Copyright (c) 2021, Arm Limited.
  All rights reserved.
 
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index d3ada13..fd9f544 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -2875,3 +2875,122 @@
 
    return 0;
 }
+
+
+static const uint8_t spExpectedForOpenBytes[] = {
+   0x50, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+   0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+   0x78
+};
+
+static const uint8_t spExpectedForOpenBytes2[] = {
+   0xA4, 0x0A, 0x16, 0x14, 0x42, 0x78, 0x78, 0x66,
+   0x74, 0x68, 0x69, 0x72, 0x74, 0x79, 0x43, 0x79,
+   0x79, 0x79, 0x18, 0x28, 0x81, 0x40
+};
+
+int32_t
+OpenCloseBytesTest(void)
+{
+   UsefulBuf_MAKE_STACK_UB(   TestBuf,  20);
+   UsefulBuf_MAKE_STACK_UB(   TestBuf2, 30);
+   QCBOREncodeContext         EC;
+   UsefulBuf                  Place;
+   UsefulBufC                 Encoded;
+   QCBORError                 uErr;
+
+   /* Normal use case -- add a byte string that fits */
+   QCBOREncode_Init(&EC, TestBuf);
+   QCBOREncode_OpenBytes(&EC, &Place);
+   if(Place.ptr != TestBuf.ptr ||
+      Place.len != TestBuf.len) {
+      return 1;
+   }
+   Place.len -= 4;
+   UsefulBuf_Set(Place, 'x');
+   QCBOREncode_CloseBytes(&EC, Place.len);
+   QCBOREncode_Finish(&EC, &Encoded);
+   if(UsefulBuf_Compare(Encoded,
+                        UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedForOpenBytes))) {
+      return 2;
+   }
+
+
+   /* Open a byte string with no room left */
+   QCBOREncode_Init(&EC, TestBuf);
+   QCBOREncode_AddSZString(&EC, "0123456789012345678");
+   QCBOREncode_OpenBytes(&EC, &Place);
+   if(Place.ptr != NULL ||
+      Place.len != 0) {
+      return 3;
+   }
+
+   /* Try to extend byte string past end of encoding output buffer */
+   QCBOREncode_Init(&EC, TestBuf);
+   QCBOREncode_AddSZString(&EC, "012345678901234567");
+   QCBOREncode_OpenBytes(&EC, &Place);
+   /* Don't bother to write any bytes*/
+   QCBOREncode_CloseBytes(&EC, Place.len+1);
+   uErr = QCBOREncode_GetErrorState(&EC);
+   if(uErr != QCBOR_ERR_BUFFER_TOO_SMALL) {
+      return 4;
+   }
+
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   /* Close a byte string without opening one. */
+   QCBOREncode_Init(&EC, TestBuf);
+   QCBOREncode_AddSZString(&EC, "012345678");
+   QCBOREncode_CloseBytes(&EC, 1);
+   uErr = QCBOREncode_GetErrorState(&EC);
+   if(uErr != QCBOR_ERR_TOO_MANY_CLOSES) {
+      return 5;
+   }
+
+   /* Forget to close a byte string */
+   QCBOREncode_Init(&EC, TestBuf);
+   QCBOREncode_AddSZString(&EC, "012345678");
+   QCBOREncode_OpenBytes(&EC, &Place);
+   uErr = QCBOREncode_Finish(&EC, &Encoded);
+   if(uErr != QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN) {
+      return 6;
+   }
+
+   /* Try to open a byte string in a byte string */
+   QCBOREncode_Init(&EC, TestBuf);
+   QCBOREncode_AddSZString(&EC, "012345678");
+   QCBOREncode_OpenBytes(&EC, &Place);
+   QCBOREncode_OpenBytes(&EC, &Place);
+   uErr = QCBOREncode_GetErrorState(&EC);
+   if(uErr != QCBOR_ERR_OPEN_BYTE_STRING) {
+      return 7;
+   }
+#endif  /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+   /* A successful case with a little complexity */
+   QCBOREncode_Init(&EC, TestBuf2);
+   QCBOREncode_OpenMap(&EC);
+      QCBOREncode_AddInt64ToMapN(&EC, 10, 22);
+      QCBOREncode_OpenBytesInMapN(&EC, 20, &Place);
+         Place.len = 2;
+         UsefulBuf_Set(Place, 'x');
+      QCBOREncode_CloseBytes(&EC, 2);
+      QCBOREncode_OpenBytesInMapSZ(&EC, "thirty", &Place);
+         Place.len = 3;
+         UsefulBuf_Set(Place, 'y');
+      QCBOREncode_CloseBytes(&EC, 3);
+      QCBOREncode_OpenArrayInMapN(&EC, 40);
+         QCBOREncode_OpenBytes(&EC, &Place);
+         QCBOREncode_CloseBytes(&EC, 0);
+      QCBOREncode_CloseArray(&EC);
+   QCBOREncode_CloseMap(&EC);
+   uErr = QCBOREncode_Finish(&EC, &Encoded);
+   if(uErr != QCBOR_SUCCESS) {
+      return 8;
+   }
+   if(UsefulBuf_Compare(Encoded,
+                        UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedForOpenBytes2))) {
+      return 9;
+   }
+
+   return 0;
+}
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index 69f694a..bac1085 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -1,6 +1,6 @@
 /*==============================================================================
  Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2022, Laurence Lundblade.
  All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -41,11 +41,6 @@
  - All the functions in qcbor_encode.h are called once in the aggregation of all
    the tests below.
 
- - All the types that are supported are given as input and parsed by these tests
-
- - There is some hostile input such as invalid lengths and CBOR too complex
-   and types this parser doesn't handle
-
  */
 
 
@@ -141,10 +136,11 @@
  */
 int32_t AllAddMethodsTest(void);
 
+
 /*
  The binary string wrapping of maps and arrays used by COSE
  */
-int32_t  BstrWrapTest(void);
+int32_t BstrWrapTest(void);
 
 
 /*
@@ -190,5 +186,11 @@
 int32_t QCBORHeadTest(void);
 
 
+/* Fully test QCBOREncode_OpenBytes(), QCBOREncode_CloseBytes()
+ * and friends.
+ */
+int32_t OpenCloseBytesTest(void);
+
+
 
 #endif /* defined(__QCBOR__qcbor_encode_tests__) */
diff --git a/test/run_tests.c b/test/run_tests.c
index a5c6635..54cd883 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -60,11 +60,13 @@
     TEST_ENTRY(UOBTest_BoundaryConditionsTest),
     TEST_ENTRY(UBMacroConversionsTest),
     TEST_ENTRY(UBUtilTests),
-    TEST_ENTRY(UIBTest_IntegerFormat)
+    TEST_ENTRY(UIBTest_IntegerFormat),
+    TEST_ENTRY(UBAdvanceTest)
 };
 
 
 static test_entry s_tests[] = {
+    TEST_ENTRY(OpenCloseBytesTest),
     TEST_ENTRY(EnterBstrTest),
     TEST_ENTRY(IntegerConvertTest),
     TEST_ENTRY(EnterMapTest),