Evolve the CBOR writer API

Treat struct CborOut as opaque in the API and add accessor functions for
the useful properties.

Remove error handling from each of the writing functions and instead
check for errors explicity on the struct CborOut. This encourages code
that optimisitically attempts to write everything to the buffer, the
common case, and only checks for success once complete. The space that
would have been neededi to encode everything is always captured to allow
for measurement. Code size also reduces a little as a result.

Change-Id: Idb35719087cb23eb6aafba1d11597c1b57f4f437
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/46140
Commit-Queue: Andrew Scull <ascull@google.com>
Pigweed-Auto-Submit: Andrew Scull <ascull@google.com>
Reviewed-by: Darren Krahn <dkrahn@google.com>
diff --git a/include/dice/cbor_writer.h b/include/dice/cbor_writer.h
index 9835c6e..700d57f 100644
--- a/include/dice/cbor_writer.h
+++ b/include/dice/cbor_writer.h
@@ -15,6 +15,7 @@
 #ifndef DICE_CBOR_WRITER_H_
 #define DICE_CBOR_WRITER_H_
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -24,32 +25,49 @@
 
 struct CborOut {
   uint8_t* buffer;
-  size_t size;
-  size_t offset;
+  size_t buffer_size;
+  size_t cursor;
 };
 
+// Initializes an output stream for writing CBOR tokens.
+static inline void CborOutInit(uint8_t* buffer, size_t buffer_size,
+                               struct CborOut* out) {
+  out->buffer = buffer;
+  out->buffer_size = buffer_size;
+  out->cursor = 0;
+}
+
+// Returns the number of bytes of encoded data. If |CborOutOverflowed()|
+// returns false, this number of bytes have been written, otherwise, this is the
+// number of bytes that that would have been written had there been space.
+static inline size_t CborOutSize(const struct CborOut* out) {
+  return out->cursor;
+}
+
+// Returns whether the |out| buffer contains the encoded tokens written to it or
+// whether the encoded tokens did not fit and the contents of the buffer should
+// be considered invalid.
+static inline bool CborOutOverflowed(const struct CborOut* out) {
+  return out->cursor == SIZE_MAX || out->cursor > out->buffer_size;
+}
+
 // These functions write simple deterministically encoded CBOR tokens to an
-// output buffer. If a NULL buffer is provided, nothing is written but the
-// offset is still increased and the size returned to allow for measurement of
-// the encoded data.
+// output buffer. The offset is always increased, even if there is not enough
+// space in the output buffer to allow for measurement of the encoded data.
+// Use |CborOutOverflowed()| to check whether or not the buffer successfully
+// contains all of the of the encoded data.
 //
 // Complex types are constructed from these simple types, see RFC 8949. The
 // caller is responsible for correct and deterministic encoding of complex
 // types.
-//
-// If the encoding would overflow the offset or cannot be written to the
-// remaining space in non-null buffer, 0 is returned and the output stream must
-// be considered corrupted as there may have been a partial update to the
-// output.
-size_t CborWriteInt(int64_t val, struct CborOut* out);
-size_t CborWriteBstr(size_t data_size, const uint8_t* data,
-                     struct CborOut* out);
-size_t CborWriteTstr(const char* str, struct CborOut* out);
-size_t CborWriteArray(size_t num_elements, struct CborOut* out);
-size_t CborWriteMap(size_t num_pairs, struct CborOut* out);
-size_t CborWriteFalse(struct CborOut* out);
-size_t CborWriteTrue(struct CborOut* out);
-size_t CborWriteNull(struct CborOut* out);
+void CborWriteInt(int64_t val, struct CborOut* out);
+void CborWriteBstr(size_t data_size, const uint8_t* data, struct CborOut* out);
+void CborWriteTstr(const char* str, struct CborOut* out);
+void CborWriteArray(size_t num_elements, struct CborOut* out);
+void CborWriteMap(size_t num_pairs, struct CborOut* out);
+void CborWriteFalse(struct CborOut* out);
+void CborWriteTrue(struct CborOut* out);
+void CborWriteNull(struct CborOut* out);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/src/cbor_cert_op.c b/src/cbor_cert_op.c
index b8ed738..c3e6c91 100644
--- a/src/cbor_cert_op.c
+++ b/src/cbor_cert_op.c
@@ -35,17 +35,16 @@
   const int64_t kCoseHeaderAlgLabel = 1;
   const int64_t kCoseAlgEdDSA = -8;
 
-  struct CborOut out = {
-      .buffer = buffer,
-      .size = buffer_size,
-  };
-  if (!CborWriteMap(/*num_elements=*/1, &out) ||
-      // Add the algorithm.
-      !CborWriteInt(kCoseHeaderAlgLabel, &out) ||
-      !CborWriteInt(kCoseAlgEdDSA, &out)) {
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  CborWriteMap(/*num_elements=*/1, &out);
+  // Add the algorithm.
+  CborWriteInt(kCoseHeaderAlgLabel, &out);
+  CborWriteInt(kCoseAlgEdDSA, &out);
+  if (CborOutOverflowed(&out)) {
     return kDiceResultBufferTooSmall;
   }
-  *encoded_size = out.offset;
+  *encoded_size = CborOutSize(&out);
   return kDiceResultOk;
 }
 
@@ -63,30 +62,29 @@
   const int64_t kCoseKeyOpsVerify = 2;
   const int64_t kCoseCrvEd25519 = 6;
 
-  struct CborOut out = {
-      .buffer = buffer,
-      .size = buffer_size,
-  };
-  if (!CborWriteMap(/*num_pairs=*/5, &out) ||
-      // Add the key type.
-      !CborWriteInt(kCoseKeyKtyLabel, &out) ||
-      !CborWriteInt(kCoseKeyTypeOkp, &out) ||
-      // Add the algorithm.
-      !CborWriteInt(kCoseKeyAlgLabel, &out) ||
-      !CborWriteInt(kCoseAlgEdDSA, &out) ||
-      // Add the KeyOps.
-      !CborWriteInt(kCoseKeyOpsLabel, &out) ||
-      !CborWriteArray(/*num_elements=*/1, &out) ||
-      !CborWriteInt(kCoseKeyOpsVerify, &out) ||
-      // Add the curve.
-      !CborWriteInt(kCoseOkpCrvLabel, &out) ||
-      !CborWriteInt(kCoseCrvEd25519, &out) ||
-      // Add the subject public key.
-      !CborWriteInt(kCoseOkpXLabel, &out) ||
-      !CborWriteBstr(/*data_size=*/32, subject_public_key, &out)) {
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  CborWriteMap(/*num_pairs=*/5, &out);
+  // Add the key type.
+  CborWriteInt(kCoseKeyKtyLabel, &out);
+  CborWriteInt(kCoseKeyTypeOkp, &out);
+  // Add the algorithm.
+  CborWriteInt(kCoseKeyAlgLabel, &out);
+  CborWriteInt(kCoseAlgEdDSA, &out);
+  // Add the KeyOps.
+  CborWriteInt(kCoseKeyOpsLabel, &out);
+  CborWriteArray(/*num_elements=*/1, &out);
+  CborWriteInt(kCoseKeyOpsVerify, &out);
+  // Add the curve.
+  CborWriteInt(kCoseOkpCrvLabel, &out);
+  CborWriteInt(kCoseCrvEd25519, &out);
+  // Add the subject public key.
+  CborWriteInt(kCoseOkpXLabel, &out);
+  CborWriteBstr(/*data_size=*/32, subject_public_key, &out);
+  if (CborOutOverflowed(&out)) {
     return kDiceResultBufferTooSmall;
   }
-  *encoded_size = out.offset;
+  *encoded_size = CborOutSize(&out);
   return kDiceResultOk;
 }
 
@@ -129,29 +127,23 @@
     map_pairs += 1;
   }
 
-  struct CborOut out = {
-      .buffer = buffer,
-      .size = buffer_size,
-  };
-  if (!CborWriteMap(map_pairs, &out) ||
-      // Add the issuer.
-      !CborWriteInt(kCwtIssuerLabel, &out) ||
-      !CborWriteTstr(authority_id_hex, &out) ||
-      // Add the subject.
-      !CborWriteInt(kCwtSubjectLabel, &out) ||
-      !CborWriteTstr(subject_id_hex, &out) ||
-      // Add the code hash.
-      !CborWriteInt(kCodeHashLabel, &out) ||
-      !CborWriteBstr(DICE_HASH_SIZE, input_values->code_hash, &out)) {
-    return kDiceResultBufferTooSmall;
-  }
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  CborWriteMap(map_pairs, &out);
+  // Add the issuer.
+  CborWriteInt(kCwtIssuerLabel, &out);
+  CborWriteTstr(authority_id_hex, &out);
+  // Add the subject.
+  CborWriteInt(kCwtSubjectLabel, &out);
+  CborWriteTstr(subject_id_hex, &out);
+  // Add the code hash.
+  CborWriteInt(kCodeHashLabel, &out);
+  CborWriteBstr(DICE_HASH_SIZE, input_values->code_hash, &out);
   // Add the code descriptor, if provided.
   if (input_values->code_descriptor_size > 0) {
-    if (!CborWriteInt(kCodeDescriptorLabel, &out) ||
-        !CborWriteBstr(input_values->code_descriptor_size,
-                       input_values->code_descriptor, &out)) {
-      return kDiceResultBufferTooSmall;
-    }
+    CborWriteInt(kCodeDescriptorLabel, &out);
+    CborWriteBstr(input_values->code_descriptor_size,
+                  input_values->code_descriptor, &out);
   }
   // Add the config inputs.
   if (input_values->config_type == kDiceConfigTypeDescriptor) {
@@ -162,52 +154,41 @@
     if (result != kDiceResultOk) {
       return result;
     }
-    if (
-        // Add the config descriptor.
-        !CborWriteInt(kConfigDescriptorLabel, &out) ||
-        !CborWriteBstr(input_values->config_descriptor_size,
-                       input_values->config_descriptor, &out) ||
-        // Add the Config hash.
-        !CborWriteInt(kConfigHashLabel, &out) ||
-        !CborWriteBstr(DICE_HASH_SIZE, config_descriptor_hash, &out)) {
-      return kDiceResultBufferTooSmall;
-    }
+    // Add the config descriptor.
+    CborWriteInt(kConfigDescriptorLabel, &out);
+    CborWriteBstr(input_values->config_descriptor_size,
+                  input_values->config_descriptor, &out);
+    // Add the Config hash.
+    CborWriteInt(kConfigHashLabel, &out);
+    CborWriteBstr(DICE_HASH_SIZE, config_descriptor_hash, &out);
   } else if (input_values->config_type == kDiceConfigTypeInline) {
     // Add the inline config.
-    if (!CborWriteInt(kConfigDescriptorLabel, &out) ||
-        !CborWriteBstr(DICE_INLINE_CONFIG_SIZE, input_values->config_value,
-                       &out)) {
-      return kDiceResultBufferTooSmall;
-    }
+    CborWriteInt(kConfigDescriptorLabel, &out);
+    CborWriteBstr(DICE_INLINE_CONFIG_SIZE, input_values->config_value, &out);
   }
   // Add the authority inputs.
-  if (!CborWriteInt(kAuthorityHashLabel, &out) ||
-      !CborWriteBstr(DICE_HASH_SIZE, input_values->authority_hash, &out)) {
-    return kDiceResultBufferTooSmall;
-  }
+  CborWriteInt(kAuthorityHashLabel, &out);
+  CborWriteBstr(DICE_HASH_SIZE, input_values->authority_hash, &out);
   if (input_values->authority_descriptor_size > 0) {
-    if (!CborWriteInt(kAuthorityDescriptorLabel, &out) ||
-        !CborWriteBstr(input_values->authority_descriptor_size,
-                       input_values->authority_descriptor, &out)) {
-      return kDiceResultBufferTooSmall;
-    }
+    CborWriteInt(kAuthorityDescriptorLabel, &out);
+    CborWriteBstr(input_values->authority_descriptor_size,
+                  input_values->authority_descriptor, &out);
   }
   uint8_t mode_byte = input_values->mode;
   uint8_t key_usage = kKeyUsageCertSign;
-  if (
-      // Add the mode input.
-      !CborWriteInt(kModeLabel, &out) ||
-      !CborWriteBstr(/*data_sisze=*/1, &mode_byte, &out) ||
-      // Add the subject public key.
-      !CborWriteInt(kSubjectPublicKeyLabel, &out) ||
-      !CborWriteBstr(encoded_public_key_size, encoded_public_key, &out) ||
-      // Add the key usage.
-      !CborWriteInt(kKeyUsageLabel, &out) ||
-      !CborWriteBstr(/*data_size=*/1, &key_usage, &out)) {
+  // Add the mode input.
+  CborWriteInt(kModeLabel, &out);
+  CborWriteBstr(/*data_sisze=*/1, &mode_byte, &out);
+  // Add the subject public key.
+  CborWriteInt(kSubjectPublicKeyLabel, &out);
+  CborWriteBstr(encoded_public_key_size, encoded_public_key, &out);
+  // Add the key usage.
+  CborWriteInt(kKeyUsageLabel, &out);
+  CborWriteBstr(/*data_size=*/1, &key_usage, &out);
+  if (CborOutOverflowed(&out)) {
     return kDiceResultBufferTooSmall;
   }
-
-  *encoded_size = out.offset;
+  *encoded_size = CborOutSize(&out);
   return kDiceResultOk;
 }
 
@@ -216,24 +197,22 @@
                                 const uint8_t* payload, size_t payload_size,
                                 size_t buffer_size, uint8_t* buffer,
                                 size_t* encoded_size) {
-  struct CborOut out = {
-      .buffer = buffer,
-      .size = buffer_size,
-  };
-  if (
-      // TBS is an array of four elements.
-      !CborWriteArray(/*num_elements=*/4, &out) ||
-      // Context string field.
-      !CborWriteTstr("Signature1", &out) ||
-      // Protected attributes from COSE_Sign1.
-      !CborWriteBstr(protected_attributes_size, protected_attributes, &out) ||
-      // Empty application data.
-      !CborWriteBstr(/*data_size=*/0, /*data=*/NULL, &out) ||
-      // Payload from COSE_Sign1.
-      !CborWriteBstr(payload_size, payload, &out)) {
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  // TBS is an array of four elements.
+  CborWriteArray(/*num_elements=*/4, &out);
+  // Context string field.
+  CborWriteTstr("Signature1", &out);
+  // Protected attributes from COSE_Sign1.
+  CborWriteBstr(protected_attributes_size, protected_attributes, &out);
+  // Empty application data.
+  CborWriteBstr(/*data_size=*/0, /*data=*/NULL, &out);
+  // Payload from COSE_Sign1.
+  CborWriteBstr(payload_size, payload, &out);
+  if (CborOutOverflowed(&out)) {
     return kDiceResultBufferTooSmall;
   }
-  *encoded_size = out.offset;
+  *encoded_size = CborOutSize(&out);
   return kDiceResultOk;
 }
 
@@ -243,24 +222,22 @@
                                   const uint8_t signature[64],
                                   size_t buffer_size, uint8_t* buffer,
                                   size_t* encoded_size) {
-  struct CborOut out = {
-      .buffer = buffer,
-      .size = buffer_size,
-  };
-  if (
-      // COSE_Sign1 is an array of four elements.
-      !CborWriteArray(/*num_elements=*/4, &out) ||
-      // Protected attributes.
-      !CborWriteBstr(protected_attributes_size, protected_attributes, &out) ||
-      // Empty map for unprotected attributes.
-      !CborWriteMap(/*num_pairs=*/0, &out) ||
-      // Payload.
-      !CborWriteBstr(payload_size, payload, &out) ||
-      // Signature.
-      !CborWriteBstr(/*num_elements=*/64, signature, &out)) {
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  // COSE_Sign1 is an array of four elements.
+  CborWriteArray(/*num_elements=*/4, &out);
+  // Protected attributes.
+  CborWriteBstr(protected_attributes_size, protected_attributes, &out);
+  // Empty map for unprotected attributes.
+  CborWriteMap(/*num_pairs=*/0, &out);
+  // Payload.
+  CborWriteBstr(payload_size, payload, &out);
+  // Signature.
+  CborWriteBstr(/*num_elements=*/64, signature, &out);
+  if (CborOutOverflowed(&out)) {
     return kDiceResultBufferTooSmall;
   }
-  *encoded_size = out.offset;
+  *encoded_size = CborOutSize(&out);
   return kDiceResultOk;
 }
 
diff --git a/src/cbor_writer.c b/src/cbor_writer.c
index b5517d9..9b29f76 100644
--- a/src/cbor_writer.c
+++ b/src/cbor_writer.c
@@ -30,9 +30,17 @@
   CBOR_TYPE_SIMPLE = 7,
 };
 
-static size_t CborWriteType(enum CborType type, uint64_t val,
-                            struct CborOut* out) {
-  // Check how much space is needed.
+static bool CborWriteWouldOverflowCursor(size_t size, struct CborOut* out) {
+  return size > SIZE_MAX - out->cursor;
+}
+
+static bool CborWriteFitsInBuffer(size_t size, struct CborOut* out) {
+  return out->cursor <= out->buffer_size &&
+         size <= out->buffer_size - out->cursor;
+}
+
+static void CborWriteType(enum CborType type, uint64_t val,
+                          struct CborOut* out) {
   size_t size;
   if (val <= 23) {
     size = 1;
@@ -45,104 +53,86 @@
   } else {
     size = 9;
   }
-  // Don't allow offset to overflow.
-  if (size > SIZE_MAX - out->offset) {
-    return 0;
+  if (CborWriteWouldOverflowCursor(size, out)) {
+    out->cursor = SIZE_MAX;
+    return;
   }
-  // Only write if a buffer is provided.
-  if (out->buffer) {
-    if (out->size < out->offset + size) {
-      return 0;
-    }
+  if (CborWriteFitsInBuffer(size, out)) {
     if (size == 1) {
-      out->buffer[out->offset] = (type << 5) | val;
+      out->buffer[out->cursor] = (type << 5) | val;
     } else if (size == 2) {
-      out->buffer[out->offset] = (type << 5) | 24;
-      out->buffer[out->offset + 1] = val & 0xff;
+      out->buffer[out->cursor] = (type << 5) | 24;
+      out->buffer[out->cursor + 1] = val & 0xff;
     } else if (size == 3) {
-      out->buffer[out->offset] = (type << 5) | 25;
-      out->buffer[out->offset + 1] = (val >> 8) & 0xff;
-      out->buffer[out->offset + 2] = val & 0xff;
+      out->buffer[out->cursor] = (type << 5) | 25;
+      out->buffer[out->cursor + 1] = (val >> 8) & 0xff;
+      out->buffer[out->cursor + 2] = val & 0xff;
     } else if (size == 5) {
-      out->buffer[out->offset] = (type << 5) | 26;
-      out->buffer[out->offset + 1] = (val >> 24) & 0xff;
-      out->buffer[out->offset + 2] = (val >> 16) & 0xff;
-      out->buffer[out->offset + 3] = (val >> 8) & 0xff;
-      out->buffer[out->offset + 4] = val & 0xff;
+      out->buffer[out->cursor] = (type << 5) | 26;
+      out->buffer[out->cursor + 1] = (val >> 24) & 0xff;
+      out->buffer[out->cursor + 2] = (val >> 16) & 0xff;
+      out->buffer[out->cursor + 3] = (val >> 8) & 0xff;
+      out->buffer[out->cursor + 4] = val & 0xff;
     } else if (size == 9) {
-      out->buffer[out->offset] = (type << 5) | 27;
-      out->buffer[out->offset + 1] = (val >> 56) & 0xff;
-      out->buffer[out->offset + 2] = (val >> 48) & 0xff;
-      out->buffer[out->offset + 3] = (val >> 40) & 0xff;
-      out->buffer[out->offset + 4] = (val >> 32) & 0xff;
-      out->buffer[out->offset + 5] = (val >> 24) & 0xff;
-      out->buffer[out->offset + 6] = (val >> 16) & 0xff;
-      out->buffer[out->offset + 7] = (val >> 8) & 0xff;
-      out->buffer[out->offset + 8] = val & 0xff;
-    } else {
-      return 0;
+      out->buffer[out->cursor] = (type << 5) | 27;
+      out->buffer[out->cursor + 1] = (val >> 56) & 0xff;
+      out->buffer[out->cursor + 2] = (val >> 48) & 0xff;
+      out->buffer[out->cursor + 3] = (val >> 40) & 0xff;
+      out->buffer[out->cursor + 4] = (val >> 32) & 0xff;
+      out->buffer[out->cursor + 5] = (val >> 24) & 0xff;
+      out->buffer[out->cursor + 6] = (val >> 16) & 0xff;
+      out->buffer[out->cursor + 7] = (val >> 8) & 0xff;
+      out->buffer[out->cursor + 8] = val & 0xff;
     }
   }
-  // Update the offset with the size it needs.
-  out->offset += size;
-  return size;
+  out->cursor += size;
 }
 
-static size_t CborWriteStr(enum CborType type, size_t data_size,
-                           const uint8_t* data, struct CborOut* out) {
-  // Write the type.
-  size_t type_size = CborWriteType(type, data_size, out);
-  if (type_size == 0) {
-    return 0;
+static void CborWriteStr(enum CborType type, size_t data_size,
+                         const uint8_t* data, struct CborOut* out) {
+  CborWriteType(type, data_size, out);
+  if (CborWriteWouldOverflowCursor(data_size, out)) {
+    out->cursor = SIZE_MAX;
+    return;
   }
-  // Don't allow offset to overflow.
-  if (data_size > SIZE_MAX - out->offset) {
-    return 0;
+  if (CborWriteFitsInBuffer(data_size, out) && data_size) {
+    memcpy(&out->buffer[out->cursor], data, data_size);
   }
-  // Write the data if a buffer is provided.
-  if (data_size > 0 && out->buffer) {
-    if (out->size < out->offset + data_size) {
-      return 0;
-    }
-    memcpy(&out->buffer[out->offset], data, data_size);
-  }
-  // Update the offset with the size it needs.
-  out->offset += data_size;
-  return type_size + data_size;
+  out->cursor += data_size;
 }
 
-size_t CborWriteInt(int64_t val, struct CborOut* out) {
+void CborWriteInt(int64_t val, struct CborOut* out) {
   if (val < 0) {
-    return CborWriteType(CBOR_TYPE_NINT, (-1 - val), out);
+    CborWriteType(CBOR_TYPE_NINT, (-1 - val), out);
+  } else {
+    CborWriteType(CBOR_TYPE_UINT, val, out);
   }
-  return CborWriteType(CBOR_TYPE_UINT, val, out);
 }
 
-size_t CborWriteBstr(size_t data_size, const uint8_t* data,
-                     struct CborOut* out) {
-  return CborWriteStr(CBOR_TYPE_BSTR, data_size, data, out);
+void CborWriteBstr(size_t data_size, const uint8_t* data, struct CborOut* out) {
+  CborWriteStr(CBOR_TYPE_BSTR, data_size, data, out);
 }
 
-size_t CborWriteTstr(const char* str, struct CborOut* out) {
-  return CborWriteStr(CBOR_TYPE_TSTR, strlen(str), (const uint8_t*)str, out);
+void CborWriteTstr(const char* str, struct CborOut* out) {
+  CborWriteStr(CBOR_TYPE_TSTR, strlen(str), (const uint8_t*)str, out);
 }
 
-size_t CborWriteArray(size_t num_elements, struct CborOut* out) {
-  return CborWriteType(CBOR_TYPE_ARRAY, num_elements, out);
+void CborWriteArray(size_t num_elements, struct CborOut* out) {
+  CborWriteType(CBOR_TYPE_ARRAY, num_elements, out);
 }
 
-size_t CborWriteMap(size_t num_pairs, struct CborOut* out) {
-  return CborWriteType(CBOR_TYPE_MAP, num_pairs, out);
+void CborWriteMap(size_t num_pairs, struct CborOut* out) {
+  CborWriteType(CBOR_TYPE_MAP, num_pairs, out);
 }
 
-size_t CborWriteFalse(struct CborOut* out) {
-  return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/20, out);
+void CborWriteFalse(struct CborOut* out) {
+  CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/20, out);
 }
 
-size_t CborWriteTrue(struct CborOut* out) {
-  return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/21, out);
+void CborWriteTrue(struct CborOut* out) {
+  CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/21, out);
 }
 
-size_t CborWriteNull(struct CborOut* out) {
-  return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/22, out);
+void CborWriteNull(struct CborOut* out) {
+  CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/22, out);
 }
diff --git a/src/cbor_writer_fuzzer.cc b/src/cbor_writer_fuzzer.cc
index fc1b739..bb4b3bd 100644
--- a/src/cbor_writer_fuzzer.cc
+++ b/src/cbor_writer_fuzzer.cc
@@ -41,11 +41,8 @@
 
   auto buffer_size = fdp.ConsumeIntegralInRange<size_t>(0, kMaxBufferSize);
   std::vector<uint8_t> buffer(buffer_size);
-  CborOut out = {
-      .buffer = buffer_size == 0 ? nullptr : buffer.data(),
-      .size = buffer_size,
-      .offset = fdp.ConsumeIntegral<size_t>(),
-  };
+  CborOut out;
+  CborOutInit(buffer.data(), buffer.size(), &out);
 
   for (size_t i = 0; i < kIterations; i++) {
     switch (fdp.ConsumeEnum<CborWriterFunction>()) {
diff --git a/src/cbor_writer_test.cc b/src/cbor_writer_test.cc
index ed0cdc8..1bcfd59 100644
--- a/src/cbor_writer_test.cc
+++ b/src/cbor_writer_test.cc
@@ -23,28 +23,26 @@
 TEST(CborWriterTest, Int1ByteEncoding) {
   const uint8_t kExpectedEncoding[] = {0, 23, 0x20, 0x37};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(1u, CborWriteInt(0, &out));
-  EXPECT_EQ(1u, CborWriteInt(23, &out));
-  EXPECT_EQ(1u, CborWriteInt(-1, &out));
-  EXPECT_EQ(1u, CborWriteInt(-24, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteInt(0, &out);
+  CborWriteInt(23, &out);
+  CborWriteInt(-1, &out);
+  CborWriteInt(-24, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
 TEST(CborWriterTest, Int2Bytes) {
   const uint8_t kExpectedEncoding[] = {24, 24, 24, 0xff, 0x38, 24, 0x38, 0xff};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(2u, CborWriteInt(24, &out));
-  EXPECT_EQ(2u, CborWriteInt(0xff, &out));
-  EXPECT_EQ(2u, CborWriteInt(-25, &out));
-  EXPECT_EQ(2u, CborWriteInt(-0x100, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteInt(24, &out);
+  CborWriteInt(0xff, &out);
+  CborWriteInt(-25, &out);
+  CborWriteInt(-0x100, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
@@ -52,14 +50,13 @@
   const uint8_t kExpectedEncoding[] = {25,   0x01, 0x00, 25,   0xff, 0xff,
                                        0x39, 0x01, 0x00, 0x39, 0xff, 0xff};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(3u, CborWriteInt(0x100, &out));
-  EXPECT_EQ(3u, CborWriteInt(0xffff, &out));
-  EXPECT_EQ(3u, CborWriteInt(-0x101, &out));
-  EXPECT_EQ(3u, CborWriteInt(-0x10000, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteInt(0x100, &out);
+  CborWriteInt(0xffff, &out);
+  CborWriteInt(-0x101, &out);
+  CborWriteInt(-0x10000, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
@@ -68,14 +65,13 @@
                                        0xff, 0xff, 0xff, 0x3a, 0x00, 0x01, 0x00,
                                        0x00, 0x3a, 0xff, 0xff, 0xff, 0xff};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(5u, CborWriteInt(0x10000, &out));
-  EXPECT_EQ(5u, CborWriteInt(0xffffffff, &out));
-  EXPECT_EQ(5u, CborWriteInt(-0x10001, &out));
-  EXPECT_EQ(5u, CborWriteInt(-0x100000000, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteInt(0x10000, &out);
+  CborWriteInt(0xffffffff, &out);
+  CborWriteInt(-0x10001, &out);
+  CborWriteInt(-0x100000000, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
@@ -85,14 +81,13 @@
       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3b, 0x00, 0x00, 0x00, 0x01, 0x00,
       0x00, 0x00, 0x00, 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(9u, CborWriteInt(0x100000000, &out));
-  EXPECT_EQ(9u, CborWriteInt(INT64_MAX, &out));
-  EXPECT_EQ(9u, CborWriteInt(-0x100000001, &out));
-  EXPECT_EQ(9u, CborWriteInt(INT64_MIN, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteInt(0x100000000, &out);
+  CborWriteInt(INT64_MAX, &out);
+  CborWriteInt(-0x100000001, &out);
+  CborWriteInt(INT64_MIN, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
@@ -102,13 +97,12 @@
       0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
   };
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(3u, CborWriteInt(0x1234, &out));
-  EXPECT_EQ(5u, CborWriteInt(0x12345678, &out));
-  EXPECT_EQ(9u, CborWriteInt(0x123456789abcdef0, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteInt(0x1234, &out);
+  CborWriteInt(0x12345678, &out);
+  CborWriteInt(0x123456789abcdef0, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
@@ -116,226 +110,253 @@
   const uint8_t kExpectedEncoding[] = {0x45, 'h', 'e', 'l', 'l', 'o'};
   const uint8_t kData[] = {'h', 'e', 'l', 'l', 'o'};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(sizeof(kExpectedEncoding),
-            CborWriteBstr(sizeof(kData), kData, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(kData), kData, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
 TEST(CborWriterTest, TstrEncoding) {
   const uint8_t kExpectedEncoding[] = {0x65, 'w', 'o', 'r', 'l', 'd'};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(sizeof(kExpectedEncoding), CborWriteTstr("world", &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteTstr("world", &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
 TEST(CborWriterTest, ArrayEncoding) {
   const uint8_t kExpectedEncoding[] = {0x98, 29};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(sizeof(kExpectedEncoding),
-            CborWriteArray(/*num_elements=*/29, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteArray(/*num_elements=*/29, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
 TEST(CborWriterTest, MapEncoding) {
   const uint8_t kExpectedEncoding[] = {0xb9, 0x02, 0x50};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(sizeof(kExpectedEncoding), CborWriteMap(/*num_pairs=*/592, &out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteMap(/*num_pairs=*/592, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
 TEST(CborWriterTest, FalseEncoding) {
   const uint8_t kExpectedEncoding[] = {0xf4};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(sizeof(kExpectedEncoding), CborWriteFalse(&out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteFalse(&out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
 TEST(CborWriterTest, TrueEncoding) {
   const uint8_t kExpectedEncoding[] = {0xf5};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(sizeof(kExpectedEncoding), CborWriteTrue(&out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteTrue(&out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
 TEST(CborWriterTest, NullEncoding) {
   const uint8_t kExpectedEncoding[] = {0xf6};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(sizeof(kExpectedEncoding), CborWriteNull(&out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteNull(&out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
 TEST(CborWriterTest, CborOutInvariants) {
   const uint8_t kData[] = {0xb2, 0x34, 0x75, 0x92, 0x52};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  EXPECT_EQ(3u, CborWriteInt(0xab34, &out));
-  EXPECT_EQ(6u, CborWriteBstr(sizeof(kData), kData, &out));
-  EXPECT_EQ(9u, CborWriteTstr("A string", &out));
-  EXPECT_EQ(1u, CborWriteArray(/*num_elements=*/16, &out));
-  EXPECT_EQ(2u, CborWriteMap(/*num_pairs=*/35, &out));
-  EXPECT_EQ(1u, CborWriteFalse(&out));
-  EXPECT_EQ(1u, CborWriteTrue(&out));
-  EXPECT_EQ(1u, CborWriteNull(&out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteInt(0xab34, &out);
+  CborWriteBstr(sizeof(kData), kData, &out);
+  CborWriteTstr("A string", &out);
+  CborWriteArray(/*num_elements=*/16, &out);
+  CborWriteMap(/*num_pairs=*/35, &out);
+  CborWriteFalse(&out);
+  CborWriteTrue(&out);
+  CborWriteNull(&out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
   // Offset is the cumulative size.
-  EXPECT_EQ(3 + 6 + 9 + 1 + 2 + 1 + 1 + 1u, out.offset);
-  // Buffer and size are unchanged.
-  EXPECT_EQ(buffer, out.buffer);
-  EXPECT_EQ(sizeof(buffer), out.size);
+  EXPECT_EQ(3 + 6 + 9 + 1 + 2 + 1 + 1 + 1u, CborOutSize(&out));
 }
 
 TEST(CborWriterTest, NullBufferForMeasurement) {
   const uint8_t kData[] = {16, 102, 246, 12, 156, 35, 84};
-  CborOut out = {
-      .buffer = nullptr,
-      .size = 2,  // Ignored.
-  };
-  EXPECT_EQ(1u, CborWriteNull(&out));
-  EXPECT_EQ(1u, CborWriteTrue(&out));
-  EXPECT_EQ(1u, CborWriteFalse(&out));
-  EXPECT_EQ(3u, CborWriteMap(/*num_pairs=*/623, &out));
-  EXPECT_EQ(5u, CborWriteArray(/*num_elements=*/70000, &out));
-  EXPECT_EQ(7u, CborWriteTstr("length", &out));
-  EXPECT_EQ(8u, CborWriteBstr(sizeof(kData), kData, &out));
-  EXPECT_EQ(5u, CborWriteInt(-10002000, &out));
+  CborOut out;
+  CborOutInit(nullptr, 0, &out);
+  CborWriteNull(&out);
+  CborWriteTrue(&out);
+  CborWriteFalse(&out);
+  CborWriteMap(/*num_pairs=*/623, &out);
+  CborWriteArray(/*num_elements=*/70000, &out);
+  CborWriteTstr("length", &out);
+  CborWriteBstr(sizeof(kData), kData, &out);
+  CborWriteInt(-10002000, &out);
+  // Measurement has occurred, but output did not.
+  EXPECT_TRUE(CborOutOverflowed(&out));
   // Offset is the cumulative size.
-  EXPECT_EQ(1 + 1 + 1 + 3 + 5 + 7 + 8 + 5u, out.offset);
-  // Buffer and size are unchanged.
-  EXPECT_EQ(nullptr, out.buffer);
-  EXPECT_EQ(2u, out.size);
+  EXPECT_EQ(1 + 1 + 1 + 3 + 5 + 7 + 8 + 5u, CborOutSize(&out));
 }
 
 TEST(CborWriterTest, BufferTooSmall) {
   const uint8_t kData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
   uint8_t buffer[1];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
   // Reset offset each time as it may be corrupted on failures.
-  out.offset = 0;
-  EXPECT_EQ(0u, CborWriteInt(-55667788, &out));
-  out.offset = 0;
-  EXPECT_EQ(0u, CborWriteBstr(sizeof(kData), kData, &out));
-  out.offset = 0;
-  EXPECT_EQ(0u, CborWriteTstr("Buffer too small", &out));
-  out.offset = 0;
-  EXPECT_EQ(0u, CborWriteArray(/*num_elements=*/563, &out));
-  out.offset = 0;
-  EXPECT_EQ(0u, CborWriteMap(/*num_pairs=*/29, &out));
-  out.size = 0;
-  out.offset = 0;
-  EXPECT_EQ(0u, CborWriteFalse(&out));
-  out.offset = 0;
-  EXPECT_EQ(0u, CborWriteTrue(&out));
-  out.offset = 0;
-  EXPECT_EQ(0u, CborWriteNull(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteInt(-55667788, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(kData), kData, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteTstr("Buffer too small", &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteArray(/*num_elements=*/563, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteMap(/*num_pairs=*/29, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, 0, &out);
+  CborWriteFalse(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, 0, &out);
+  CborWriteTrue(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, 0, &out);
+  CborWriteNull(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
 }
 
 TEST(CborWriterTest, NotEnoughRemainingSpace) {
   const uint8_t kData[] = {0xff, 0xee, 0xdd, 0xcc};
+  uint8_t zeros[64] = {0};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  // Reset offset each time as it may be corrupted on failures.
-  out.offset = sizeof(buffer) - 1;
-  EXPECT_EQ(0u, CborWriteInt(-36, &out));
-  out.offset = sizeof(buffer) - 1;
-  EXPECT_EQ(0u, CborWriteBstr(sizeof(kData), kData, &out));
-  out.offset = sizeof(buffer) - 1;
-  EXPECT_EQ(0u, CborWriteTstr("Won't fit", &out));
-  out.offset = sizeof(buffer) - 1;
-  EXPECT_EQ(0u, CborWriteArray(/*num_elements=*/352, &out));
-  out.offset = sizeof(buffer) - 1;
-  EXPECT_EQ(0u, CborWriteMap(/*num_pairs=*/73, &out));
-  out.offset = sizeof(buffer);
-  EXPECT_EQ(0u, CborWriteFalse(&out));
-  out.offset = sizeof(buffer);
-  EXPECT_EQ(0u, CborWriteTrue(&out));
-  out.offset = sizeof(buffer);
-  EXPECT_EQ(0u, CborWriteNull(&out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(buffer) - 3, zeros, &out);
+  CborWriteInt(-36, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(buffer) - 3, zeros, &out);
+  CborWriteBstr(sizeof(kData), kData, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(buffer) - 3, zeros, &out);
+  CborWriteTstr("Won't fit", &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(buffer) - 3, zeros, &out);
+  CborWriteArray(/*num_elements=*/352, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(buffer) - 3, zeros, &out);
+  CborWriteMap(/*num_pairs=*/73, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(buffer) - 2, zeros, &out);
+  CborWriteFalse(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(buffer) - 2, zeros, &out);
+  CborWriteTrue(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(sizeof(buffer) - 2, zeros, &out);
+  CborWriteNull(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
 }
 
 TEST(CborWriterTest, OffsetOverflow) {
   const uint8_t kData[] = {0xff, 0xee, 0xdd, 0xcc};
   uint8_t buffer[64];
-  CborOut out = {
-      .buffer = buffer,
-      .size = sizeof(buffer),
-  };
-  // Reset offset each time as it may be corrupted on failures.
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteInt(0x234198adb, &out));
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteBstr(sizeof(kData), kData, &out));
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteTstr("Overflow", &out));
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteArray(/*num_elements=*/41, &out));
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteMap(/*num_pairs=*/998844, &out));
-  out.offset = SIZE_MAX;
-  EXPECT_EQ(0u, CborWriteFalse(&out));
-  out.offset = SIZE_MAX;
-  EXPECT_EQ(0u, CborWriteTrue(&out));
-  out.offset = SIZE_MAX;
-  EXPECT_EQ(0u, CborWriteNull(&out));
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteInt(0x234198adb, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteBstr(sizeof(kData), kData, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteTstr("Overflow", &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteArray(/*num_elements=*/41, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteMap(/*num_pairs=*/998844, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(SIZE_MAX - 9, nullptr, &out);
+  CborWriteFalse(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(SIZE_MAX - 9, nullptr, &out);
+  CborWriteTrue(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteBstr(SIZE_MAX - 9, nullptr, &out);
+  CborWriteNull(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
 }
 
 TEST(CborWriterTest, MeasurementOffsetOverflow) {
   const uint8_t kData[] = {0xf0, 0x0f, 0xca, 0xfe, 0xfe, 0xed};
-  CborOut out = {
-      .buffer = nullptr,
-  };
-  // Reset offset each time as it may be corrupted on failures.
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteInt(0x1419823646241245, &out));
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteBstr(sizeof(kData), kData, &out));
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteTstr("Measured overflow", &out));
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteArray(/*num_elements=*/8368257314, &out));
-  out.offset = SIZE_MAX - 1;
-  EXPECT_EQ(0u, CborWriteMap(/*num_pairs=*/92, &out));
-  out.offset = SIZE_MAX;
-  EXPECT_EQ(0u, CborWriteFalse(&out));
-  out.offset = SIZE_MAX;
-  EXPECT_EQ(0u, CborWriteTrue(&out));
-  out.offset = SIZE_MAX;
-  EXPECT_EQ(0u, CborWriteNull(&out));
+  CborOut out;
+  CborOutInit(nullptr, 0, &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteInt(0x1419823646241245, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(nullptr, 0, &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteBstr(sizeof(kData), kData, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(nullptr, 0, &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteTstr("Measured overflow", &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(nullptr, 0, &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteArray(/*num_elements=*/8368257314, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(nullptr, 0, &out);
+  CborWriteBstr(SIZE_MAX - 10, nullptr, &out);
+  CborWriteMap(/*num_pairs=*/92, &out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(nullptr, 0, &out);
+  CborWriteBstr(SIZE_MAX - 9, nullptr, &out);
+  CborWriteFalse(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(nullptr, 0, &out);
+  CborWriteBstr(SIZE_MAX - 9, nullptr, &out);
+  CborWriteTrue(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
+  CborOutInit(nullptr, 0, &out);
+  CborWriteBstr(SIZE_MAX - 9, nullptr, &out);
+  CborWriteNull(&out);
+  EXPECT_TRUE(CborOutOverflowed(&out));
 }
 }