Add std/cbor
diff --git a/doc/changelog.md b/doc/changelog.md
index b320e7f..8f04555 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -13,6 +13,7 @@
 - Added `example/imageviewer`.
 - Added `example/jsonptr`.
 - Added `std/bmp`.
+- Added `std/cbor`.
 - Added `std/gif.config_decoder`.
 - Added `std/json`.
 - Added `std/wbmp`.
diff --git a/example/jsonptr/jsonptr.cc b/example/jsonptr/jsonptr.cc
index b476875..b5f86b6 100644
--- a/example/jsonptr/jsonptr.cc
+++ b/example/jsonptr/jsonptr.cc
@@ -121,6 +121,7 @@
 // code simply isn't compiled.
 #define WUFFS_CONFIG__MODULES
 #define WUFFS_CONFIG__MODULE__BASE
+#define WUFFS_CONFIG__MODULE__CBOR
 #define WUFFS_CONFIG__MODULE__JSON
 
 // If building this program in an environment that doesn't easily accommodate
@@ -152,6 +153,7 @@
     "Flags:\n"
     "    -c      -compact-output\n"
     "    -d=NUM  -max-output-depth=NUM\n"
+    "    -i=FMT  -input-format={json,cbor}\n"
     "    -o=FMT  -output-format={json,cbor}\n"
     "    -q=STR  -query=STR\n"
     "    -s=NUM  -spaces=NUM\n"
@@ -316,7 +318,9 @@
 uint32_t g_suppress_write_dst;
 bool g_wrote_to_dst;
 
-wuffs_json__decoder g_dec;
+wuffs_cbor__decoder g_cbor_decoder;
+wuffs_json__decoder g_json_decoder;
+wuffs_base__token_decoder* g_dec;
 
 // cbor_output_string_array is a 4 KiB buffer. For -output-format=cbor, strings
 // whose length are 4096 or less are written as a single definite-length
@@ -597,6 +601,7 @@
 
   bool compact_output;
   bool fail_if_unsandboxed;
+  file_format input_format;
   bool input_json_extra_comma;
   uint32_t max_output_depth;
   file_format output_format;
@@ -656,6 +661,14 @@
       g_flags.fail_if_unsandboxed = true;
       continue;
     }
+    if (!strcmp(arg, "i=cbor") || !strcmp(arg, "input-format=cbor")) {
+      g_flags.input_format = file_format::cbor;
+      continue;
+    }
+    if (!strcmp(arg, "i=json") || !strcmp(arg, "input-format=json")) {
+      g_flags.input_format = file_format::json;
+      continue;
+    }
     if (!strcmp(arg, "input-json-extra-comma")) {
       g_flags.input_json_extra_comma = true;
       continue;
@@ -747,18 +760,27 @@
   g_suppress_write_dst = g_query.next_fragment() ? 1 : 0;
   g_wrote_to_dst = false;
 
-  TRY(g_dec.initialize(sizeof__wuffs_json__decoder(), WUFFS_VERSION, 0)
-          .message());
+  if (g_flags.input_format == file_format::json) {
+    TRY(g_json_decoder
+            .initialize(sizeof__wuffs_json__decoder(), WUFFS_VERSION, 0)
+            .message());
+    g_dec = g_json_decoder.upcast_as__wuffs_base__token_decoder();
+  } else {
+    TRY(g_cbor_decoder
+            .initialize(sizeof__wuffs_cbor__decoder(), WUFFS_VERSION, 0)
+            .message());
+    g_dec = g_cbor_decoder.upcast_as__wuffs_base__token_decoder();
+  }
 
   if (g_flags.input_json_extra_comma) {
-    g_dec.set_quirk_enabled(WUFFS_JSON__QUIRK_ALLOW_EXTRA_COMMA, true);
+    g_dec->set_quirk_enabled(WUFFS_JSON__QUIRK_ALLOW_EXTRA_COMMA, true);
   }
 
   // Consume an optional whitespace trailer. This isn't part of the JSON spec,
   // but it works better with line oriented Unix tools (such as "echo 123 |
   // jsonptr" where it's "echo", not "echo -n") or hand-edited JSON files which
   // can accidentally contain trailing whitespace.
-  g_dec.set_quirk_enabled(WUFFS_JSON__QUIRK_ALLOW_TRAILING_NEW_LINE, true);
+  g_dec->set_quirk_enabled(WUFFS_JSON__QUIRK_ALLOW_TRAILING_NEW_LINE, true);
 
   return nullptr;
 }
@@ -937,11 +959,48 @@
 
 const char*  //
 write_number(uint64_t vbd, uint8_t* ptr, size_t len) {
-  if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT) {
-    if (g_flags.output_format == file_format::json) {
+  if (g_flags.output_format == file_format::json) {
+    if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT) {
       return write_dst(ptr, len);
+    } else if ((vbd &
+                WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_UNSIGNED) &&
+               (vbd &
+                WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN)) {
+      if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE) {
+        if (len == 0) {
+          goto fail;
+        }
+        ptr++;
+        len--;
+      }
+      uint64_t u;
+      switch (len) {
+        case 1:
+          u = wuffs_base__load_u8__no_bounds_check(ptr);
+          break;
+        case 2:
+          u = wuffs_base__load_u16be__no_bounds_check(ptr);
+          break;
+        case 4:
+          u = wuffs_base__load_u32be__no_bounds_check(ptr);
+          break;
+        case 8:
+          u = wuffs_base__load_u64be__no_bounds_check(ptr);
+          break;
+        default:
+          goto fail;
+      }
+      uint8_t buf[WUFFS_BASE__U64__BYTE_LENGTH__MAX_INCL];
+      size_t n = wuffs_base__render_number_u64(
+          wuffs_base__make_slice_u8(&buf[0], sizeof buf), u,
+          WUFFS_BASE__RENDER_NUMBER_XXX__DEFAULT_OPTIONS);
+      return write_dst(&buf[0], n);
     }
 
+    // From here on, (g_flags.output_format == file_format::cbor).
+  } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN) {
+    return write_dst(ptr, len);
+  } else if (vbd & WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT) {
     // First try to parse (ptr, len) as an integer. Something like
     // "1180591620717411303424" is a valid number (in the JSON sense) but will
     // overflow int64_t or uint64_t, so fall back to parsing it as a float64.
@@ -973,9 +1032,23 @@
     }
   }
 
+fail:
   return "main: internal error: unexpected write_number argument";
 }
 
+const char*  //
+write_inline_integer(uint64_t vbd, uint8_t* ptr, size_t len) {
+  if (g_flags.output_format == file_format::cbor) {
+    return write_dst(ptr, len);
+  }
+
+  uint8_t buf[WUFFS_BASE__I64__BYTE_LENGTH__MAX_INCL];
+  size_t n = wuffs_base__render_number_i64(
+      wuffs_base__make_slice_u8(&buf[0], sizeof buf), (int16_t)vbd,
+      WUFFS_BASE__RENDER_NUMBER_XXX__DEFAULT_OPTIONS);
+  return write_dst(&buf[0], n);
+}
+
 // ----
 
 uint8_t  //
@@ -1332,6 +1405,11 @@
         TRY(write_number(vbd, g_src.data.ptr + g_curr_token_end_src_index - len,
                          len));
         goto after_value;
+
+      case WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER:
+        TRY(write_inline_integer(
+            vbd, g_src.data.ptr + g_curr_token_end_src_index - len, len));
+        goto after_value;
     }
 
     // Return an error if we didn't match the (vbc, vbd) pair.
@@ -1370,7 +1448,7 @@
 
   bool start_of_token_chain = true;
   while (true) {
-    wuffs_base__status status = g_dec.decode_tokens(
+    wuffs_base__status status = g_dec->decode_tokens(
         &g_tok, &g_src,
         wuffs_base__make_slice_u8(g_work_buffer_array, WORK_BUFFER_ARRAY_SIZE));
 
diff --git a/internal/cgen/base/token-public.h b/internal/cgen/base/token-public.h
index 8dba529..362cc0e 100644
--- a/internal/cgen/base/token-public.h
+++ b/internal/cgen/base/token-public.h
@@ -63,6 +63,7 @@
 #define WUFFS_BASE__TOKEN__VBC__UNICODE_CODE_POINT 3
 #define WUFFS_BASE__TOKEN__VBC__LITERAL 4
 #define WUFFS_BASE__TOKEN__VBC__NUMBER 5
+#define WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER 6
 
 // --------
 
@@ -137,7 +138,7 @@
 // --------
 
 // For a source string of "123" or "0x9A", it is valid for a tokenizer to
-// return any one of:
+// return any combination of:
 //  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT.
 //  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED.
 //  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_UNSIGNED.
@@ -156,11 +157,14 @@
 
 // The number 300 might be represented as "\x01\x2C", "\x2C\x01\x00\x00" or
 // "300", which are big-endian, little-endian or text. For binary formats, the
-// token length discriminates e.g. u16 little-endian vs u32 little-endian.
+// token length (after adjusting for FORMAT_IGNORE_ETC) discriminates
+// e.g. u16 little-endian vs u32 little-endian.
 #define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN 0x00100
 #define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_LITTLE_ENDIAN 0x00200
 #define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT 0x00400
 
+#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE 0x01000
+
 // --------
 
 // wuffs_base__token__value returns the token's high 46 bits, sign-extended. A
diff --git a/internal/cgen/data/data.go b/internal/cgen/data/data.go
index 1530fca..f4629f0 100644
--- a/internal/cgen/data/data.go
+++ b/internal/cgen/data/data.go
@@ -569,7 +569,7 @@
 	"" +
 	"// --------\n\n#define WUFFS_BASE__TOKEN__LENGTH__MAX_INCL 0xFFFF\n\n#define WUFFS_BASE__TOKEN__VALUE__SHIFT 17\n#define WUFFS_BASE__TOKEN__VALUE_EXTENSION__SHIFT 17\n#define WUFFS_BASE__TOKEN__VALUE_MAJOR__SHIFT 42\n#define WUFFS_BASE__TOKEN__VALUE_MINOR__SHIFT 17\n#define WUFFS_BASE__TOKEN__VALUE_BASE_CATEGORY__SHIFT 38\n#define WUFFS_BASE__TOKEN__VALUE_BASE_DETAIL__SHIFT 17\n#define WUFFS_BASE__TOKEN__CONTINUED__SHIFT 16\n#define WUFFS_BASE__TOKEN__LENGTH__SHIFT 0\n\n" +
 	"" +
-	"// --------\n\n#define WUFFS_BASE__TOKEN__VBC__FILLER 0\n#define WUFFS_BASE__TOKEN__VBC__STRUCTURE 1\n#define WUFFS_BASE__TOKEN__VBC__STRING 2\n#define WUFFS_BASE__TOKEN__VBC__UNICODE_CODE_POINT 3\n#define WUFFS_BASE__TOKEN__VBC__LITERAL 4\n#define WUFFS_BASE__TOKEN__VBC__NUMBER 5\n\n" +
+	"// --------\n\n#define WUFFS_BASE__TOKEN__VBC__FILLER 0\n#define WUFFS_BASE__TOKEN__VBC__STRUCTURE 1\n#define WUFFS_BASE__TOKEN__VBC__STRING 2\n#define WUFFS_BASE__TOKEN__VBC__UNICODE_CODE_POINT 3\n#define WUFFS_BASE__TOKEN__VBC__LITERAL 4\n#define WUFFS_BASE__TOKEN__VBC__NUMBER 5\n#define WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER 6\n\n" +
 	"" +
 	"// --------\n\n#define WUFFS_BASE__TOKEN__VBD__FILLER__COMMENT_LINE 0x00001\n#define WUFFS_BASE__TOKEN__VBD__FILLER__COMMENT_BLOCK 0x00002\n\n" +
 	"" +
@@ -581,8 +581,8 @@
 	"" +
 	"// --------\n\n#define WUFFS_BASE__TOKEN__VBD__LITERAL__UNDEFINED 0x00001\n#define WUFFS_BASE__TOKEN__VBD__LITERAL__NULL 0x00002\n#define WUFFS_BASE__TOKEN__VBD__LITERAL__FALSE 0x00004\n#define WUFFS_BASE__TOKEN__VBD__LITERAL__TRUE 0x00008\n\n" +
 	"" +
-	"// --------\n\n// For a source string of \"123\" or \"0x9A\", it is valid for a tokenizer to\n// return any one of:\n//  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT.\n//  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED.\n//  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_UNSIGNED.\n//\n// For a source string of \"+123\" or \"-0x9A\", only the first two are valid.\n//\n// For a source string of \"123.\", only the first one is valid.\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT 0x00001\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED 0x00002\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_UNSIGNED 0x00004\n\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_INF 0x00010\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_INF 0x00020\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_NAN 0x00040\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_NAN 0x00080\n\n// The number 300 might be represented as \"\\x01\\x2C\", \"\\x2C\\x01\\x00\\x00\" or\n// \"300\", which are big-endian, li" +
-	"ttle-endian or text. For binary formats, the\n// token length discriminates e.g. u16 little-endian vs u32 little-endian.\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN 0x00100\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_LITTLE_ENDIAN 0x00200\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT 0x00400\n\n" +
+	"// --------\n\n// For a source string of \"123\" or \"0x9A\", it is valid for a tokenizer to\n// return any combination of:\n//  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT.\n//  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED.\n//  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_UNSIGNED.\n//\n// For a source string of \"+123\" or \"-0x9A\", only the first two are valid.\n//\n// For a source string of \"123.\", only the first one is valid.\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT 0x00001\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED 0x00002\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_UNSIGNED 0x00004\n\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_INF 0x00010\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_INF 0x00020\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_NEG_NAN 0x00040\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_POS_NAN 0x00080\n\n// The number 300 might be represented as \"\\x01\\x2C\", \"\\x2C\\x01\\x00\\x00\" or\n// \"300\", which are big-en" +
+	"dian, little-endian or text. For binary formats, the\n// token length (after adjusting for FORMAT_IGNORE_ETC) discriminates\n// e.g. u16 little-endian vs u32 little-endian.\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN 0x00100\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_LITTLE_ENDIAN 0x00200\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT 0x00400\n\n#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE 0x01000\n\n" +
 	"" +
 	"// --------\n\n// wuffs_base__token__value returns the token's high 46 bits, sign-extended. A\n// negative value means an extended token, non-negative means a simple token.\nstatic inline int64_t  //\nwuffs_base__token__value(const wuffs_base__token* t) {\n  return ((int64_t)(t->repr)) >> WUFFS_BASE__TOKEN__VALUE__SHIFT;\n}\n\n// wuffs_base__token__value_extension returns a negative value if the token was\n// not an extended token.\nstatic inline int64_t  //\nwuffs_base__token__value_extension(const wuffs_base__token* t) {\n  return (~(int64_t)(t->repr)) >> WUFFS_BASE__TOKEN__VALUE_EXTENSION__SHIFT;\n}\n\n// wuffs_base__token__value_major returns a negative value if the token was not\n// a simple token.\nstatic inline int64_t  //\nwuffs_base__token__value_major(const wuffs_base__token* t) {\n  return ((int64_t)(t->repr)) >> WUFFS_BASE__TOKEN__VALUE_MAJOR__SHIFT;\n}\n\n// wuffs_base__token__value_base_category returns a negative value if the token\n// was not a simple token.\nstatic inline int64_t  //\nwuffs_base__token__value_base_cat" +
 	"egory(const wuffs_base__token* t) {\n  return ((int64_t)(t->repr)) >> WUFFS_BASE__TOKEN__VALUE_BASE_CATEGORY__SHIFT;\n}\n\nstatic inline uint64_t  //\nwuffs_base__token__value_minor(const wuffs_base__token* t) {\n  return (t->repr >> WUFFS_BASE__TOKEN__VALUE_MINOR__SHIFT) & 0x1FFFFFF;\n}\n\nstatic inline uint64_t  //\nwuffs_base__token__value_base_detail(const wuffs_base__token* t) {\n  return (t->repr >> WUFFS_BASE__TOKEN__VALUE_BASE_DETAIL__SHIFT) & 0x1FFFFF;\n}\n\nstatic inline bool  //\nwuffs_base__token__continued(const wuffs_base__token* t) {\n  return t->repr & 0x10000;\n}\n\nstatic inline uint64_t  //\nwuffs_base__token__length(const wuffs_base__token* t) {\n  return (t->repr >> WUFFS_BASE__TOKEN__LENGTH__SHIFT) & 0xFFFF;\n}\n\n#ifdef __cplusplus\n\ninline int64_t  //\nwuffs_base__token::value() const {\n  return wuffs_base__token__value(this);\n}\n\ninline int64_t  //\nwuffs_base__token::value_extension() const {\n  return wuffs_base__token__value_extension(this);\n}\n\ninline int64_t  //\nwuffs_base__token::value_major() const {\n  retu" +
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go
index 764096a..e70125c 100644
--- a/lang/builtin/builtin.go
+++ b/lang/builtin/builtin.go
@@ -81,6 +81,7 @@
 	{t.IDU32, "3", "TOKEN__VBC__UNICODE_CODE_POINT"},
 	{t.IDU32, "4", "TOKEN__VBC__LITERAL"},
 	{t.IDU32, "5", "TOKEN__VBC__NUMBER"},
+	{t.IDU32, "6", "TOKEN__VBC__INLINE_INTEGER"},
 
 	// ----
 
@@ -139,6 +140,8 @@
 	{t.IDU32, "0x00200", "TOKEN__VBD__NUMBER__FORMAT_BINARY_LITTLE_ENDIAN"},
 	{t.IDU32, "0x00400", "TOKEN__VBD__NUMBER__FORMAT_TEXT"},
 
+	{t.IDU32, "0x01000", "TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE"},
+
 	// ----
 
 	{t.IDU32, "0xFFFD", "UNICODE__REPLACEMENT_CHARACTER"},
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 261e08c..26e6e0b 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -2133,6 +2133,7 @@
 #define WUFFS_BASE__TOKEN__VBC__UNICODE_CODE_POINT 3
 #define WUFFS_BASE__TOKEN__VBC__LITERAL 4
 #define WUFFS_BASE__TOKEN__VBC__NUMBER 5
+#define WUFFS_BASE__TOKEN__VBC__INLINE_INTEGER 6
 
 // --------
 
@@ -2207,7 +2208,7 @@
 // --------
 
 // For a source string of "123" or "0x9A", it is valid for a tokenizer to
-// return any one of:
+// return any combination of:
 //  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_FLOATING_POINT.
 //  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_SIGNED.
 //  - WUFFS_BASE__TOKEN__VBD__NUMBER__CONTENT_INTEGER_UNSIGNED.
@@ -2226,11 +2227,14 @@
 
 // The number 300 might be represented as "\x01\x2C", "\x2C\x01\x00\x00" or
 // "300", which are big-endian, little-endian or text. For binary formats, the
-// token length discriminates e.g. u16 little-endian vs u32 little-endian.
+// token length (after adjusting for FORMAT_IGNORE_ETC) discriminates
+// e.g. u16 little-endian vs u32 little-endian.
 #define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN 0x00100
 #define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_BINARY_LITTLE_ENDIAN 0x00200
 #define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_TEXT 0x00400
 
+#define WUFFS_BASE__TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE 0x01000
+
 // --------
 
 // wuffs_base__token__value returns the token's high 46 bits, sign-extended. A
@@ -5512,6 +5516,207 @@
 
 // ---------------- Status Codes
 
+extern const char wuffs_cbor__error__bad_input[];
+
+// ---------------- Public Consts
+
+// ---------------- Struct Declarations
+
+typedef struct wuffs_cbor__decoder__struct wuffs_cbor__decoder;
+
+// ---------------- Public Initializer Prototypes
+
+// For any given "wuffs_foo__bar* self", "wuffs_foo__bar__initialize(self,
+// etc)" should be called before any other "wuffs_foo__bar__xxx(self, etc)".
+//
+// Pass sizeof(*self) and WUFFS_VERSION for sizeof_star_self and wuffs_version.
+// Pass 0 (or some combination of WUFFS_INITIALIZE__XXX) for options.
+
+wuffs_base__status WUFFS_BASE__WARN_UNUSED_RESULT
+wuffs_cbor__decoder__initialize(
+    wuffs_cbor__decoder* self,
+    size_t sizeof_star_self,
+    uint64_t wuffs_version,
+    uint32_t options);
+
+size_t
+sizeof__wuffs_cbor__decoder();
+
+// ---------------- Allocs
+
+// These functions allocate and initialize Wuffs structs. They return NULL if
+// memory allocation fails. If they return non-NULL, there is no need to call
+// wuffs_foo__bar__initialize, but the caller is responsible for eventually
+// calling free on the returned pointer. That pointer is effectively a C++
+// std::unique_ptr<T, decltype(&free)>.
+
+wuffs_cbor__decoder*
+wuffs_cbor__decoder__alloc();
+
+static inline wuffs_base__token_decoder*
+wuffs_cbor__decoder__alloc_as__wuffs_base__token_decoder() {
+  return (wuffs_base__token_decoder*)(wuffs_cbor__decoder__alloc());
+}
+
+// ---------------- Upcasts
+
+static inline wuffs_base__token_decoder*
+wuffs_cbor__decoder__upcast_as__wuffs_base__token_decoder(
+    wuffs_cbor__decoder* p) {
+  return (wuffs_base__token_decoder*)p;
+}
+
+// ---------------- Public Function Prototypes
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct
+wuffs_cbor__decoder__set_quirk_enabled(
+    wuffs_cbor__decoder* self,
+    uint32_t a_quirk,
+    bool a_enabled);
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__range_ii_u64
+wuffs_cbor__decoder__workbuf_len(
+    const wuffs_cbor__decoder* self);
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status
+wuffs_cbor__decoder__decode_tokens(
+    wuffs_cbor__decoder* self,
+    wuffs_base__token_buffer* a_dst,
+    wuffs_base__io_buffer* a_src,
+    wuffs_base__slice_u8 a_workbuf);
+
+// ---------------- Struct Definitions
+
+// These structs' fields, and the sizeof them, are private implementation
+// details that aren't guaranteed to be stable across Wuffs versions.
+//
+// See https://en.wikipedia.org/wiki/Opaque_pointer#C
+
+#if defined(__cplusplus) || defined(WUFFS_IMPLEMENTATION)
+
+struct wuffs_cbor__decoder__struct {
+  // Do not access the private_impl's or private_data's fields directly. There
+  // is no API/ABI compatibility or safety guarantee if you do so. Instead, use
+  // the wuffs_foo__bar__baz functions.
+  //
+  // It is a struct, not a struct*, so that the outermost wuffs_foo__bar struct
+  // can be stack allocated when WUFFS_IMPLEMENTATION is defined.
+
+  struct {
+    uint32_t magic;
+    uint32_t active_coroutine;
+    wuffs_base__vtable vtable_for__wuffs_base__token_decoder;
+    wuffs_base__vtable null_vtable;
+
+    bool f_end_of_data;
+
+    uint32_t p_decode_tokens[1];
+  } private_impl;
+
+  struct {
+    struct {
+      uint32_t v_depth;
+      uint32_t v_token_length;
+      uint8_t v_c;
+      uint64_t scratch;
+    } s_decode_tokens[1];
+  } private_data;
+
+#ifdef __cplusplus
+#if (__cplusplus >= 201103L)
+  using unique_ptr = std::unique_ptr<wuffs_cbor__decoder, decltype(&free)>;
+
+  // On failure, the alloc_etc functions return nullptr. They don't throw.
+
+  static inline unique_ptr
+  alloc() {
+    return unique_ptr(wuffs_cbor__decoder__alloc(), &free);
+  }
+
+  static inline wuffs_base__token_decoder::unique_ptr
+  alloc_as__wuffs_base__token_decoder() {
+    return wuffs_base__token_decoder::unique_ptr(
+        wuffs_cbor__decoder__alloc_as__wuffs_base__token_decoder(), &free);
+  }
+#endif  // (__cplusplus >= 201103L)
+
+#if (__cplusplus >= 201103L) && !defined(WUFFS_IMPLEMENTATION)
+  // Disallow constructing or copying an object via standard C++ mechanisms,
+  // e.g. the "new" operator, as this struct is intentionally opaque. Its total
+  // size and field layout is not part of the public, stable, memory-safe API.
+  // Use malloc or memcpy and the sizeof__wuffs_foo__bar function instead, and
+  // call wuffs_foo__bar__baz methods (which all take a "this"-like pointer as
+  // their first argument) rather than tweaking bar.private_impl.qux fields.
+  //
+  // In C, we can just leave wuffs_foo__bar as an incomplete type (unless
+  // WUFFS_IMPLEMENTATION is #define'd). In C++, we define a complete type in
+  // order to provide convenience methods. These forward on "this", so that you
+  // can write "bar->baz(etc)" instead of "wuffs_foo__bar__baz(bar, etc)".
+  wuffs_cbor__decoder__struct() = delete;
+  wuffs_cbor__decoder__struct(const wuffs_cbor__decoder__struct&) = delete;
+  wuffs_cbor__decoder__struct& operator=(
+      const wuffs_cbor__decoder__struct&) = delete;
+
+  // As above, the size of the struct is not part of the public API, and unless
+  // WUFFS_IMPLEMENTATION is #define'd, this struct type T should be heap
+  // allocated, not stack allocated. Its size is not intended to be known at
+  // compile time, but it is unfortunately divulged as a side effect of
+  // defining C++ convenience methods. Use "sizeof__T()", calling the function,
+  // instead of "sizeof T", invoking the operator. To make the two values
+  // different, so that passing the latter will be rejected by the initialize
+  // function, we add an arbitrary amount of dead weight.
+  uint8_t dead_weight[123000000];  // 123 MB.
+#endif  // (__cplusplus >= 201103L) && !defined(WUFFS_IMPLEMENTATION)
+
+  inline wuffs_base__status WUFFS_BASE__WARN_UNUSED_RESULT
+  initialize(
+      size_t sizeof_star_self,
+      uint64_t wuffs_version,
+      uint32_t options) {
+    return wuffs_cbor__decoder__initialize(
+        this, sizeof_star_self, wuffs_version, options);
+  }
+
+  inline wuffs_base__token_decoder*
+  upcast_as__wuffs_base__token_decoder() {
+    return (wuffs_base__token_decoder*)this;
+  }
+
+  inline wuffs_base__empty_struct
+  set_quirk_enabled(
+      uint32_t a_quirk,
+      bool a_enabled) {
+    return wuffs_cbor__decoder__set_quirk_enabled(this, a_quirk, a_enabled);
+  }
+
+  inline wuffs_base__range_ii_u64
+  workbuf_len() const {
+    return wuffs_cbor__decoder__workbuf_len(this);
+  }
+
+  inline wuffs_base__status
+  decode_tokens(
+      wuffs_base__token_buffer* a_dst,
+      wuffs_base__io_buffer* a_src,
+      wuffs_base__slice_u8 a_workbuf) {
+    return wuffs_cbor__decoder__decode_tokens(this, a_dst, a_src, a_workbuf);
+  }
+
+#endif  // __cplusplus
+};  // struct wuffs_cbor__decoder__struct
+
+#endif  // defined(__cplusplus) || defined(WUFFS_IMPLEMENTATION)
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------- Status Codes
+
 // ---------------- Public Consts
 
 // ---------------- Struct Declarations
@@ -16008,6 +16213,302 @@
 
 #endif  // !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BMP)
 
+#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__CBOR)
+
+// ---------------- Status Codes Implementations
+
+const char wuffs_cbor__error__bad_input[] = "#cbor: bad input";
+const char wuffs_cbor__error__internal_error_inconsistent_i_o[] = "#cbor: internal error: inconsistent I/O";
+
+// ---------------- Private Consts
+
+// ---------------- Private Initializer Prototypes
+
+// ---------------- Private Function Prototypes
+
+// ---------------- VTables
+
+const wuffs_base__token_decoder__func_ptrs
+wuffs_cbor__decoder__func_ptrs_for__wuffs_base__token_decoder = {
+  (wuffs_base__status(*)(void*,
+      wuffs_base__token_buffer*,
+      wuffs_base__io_buffer*,
+      wuffs_base__slice_u8))(&wuffs_cbor__decoder__decode_tokens),
+  (wuffs_base__empty_struct(*)(void*,
+      uint32_t,
+      bool))(&wuffs_cbor__decoder__set_quirk_enabled),
+  (wuffs_base__range_ii_u64(*)(const void*))(&wuffs_cbor__decoder__workbuf_len),
+};
+
+// ---------------- Initializer Implementations
+
+wuffs_base__status WUFFS_BASE__WARN_UNUSED_RESULT
+wuffs_cbor__decoder__initialize(
+    wuffs_cbor__decoder* self,
+    size_t sizeof_star_self,
+    uint64_t wuffs_version,
+    uint32_t options){
+  if (!self) {
+    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
+  }
+  if (sizeof(*self) != sizeof_star_self) {
+    return wuffs_base__make_status(wuffs_base__error__bad_sizeof_receiver);
+  }
+  if (((wuffs_version >> 32) != WUFFS_VERSION_MAJOR) ||
+      (((wuffs_version >> 16) & 0xFFFF) > WUFFS_VERSION_MINOR)) {
+    return wuffs_base__make_status(wuffs_base__error__bad_wuffs_version);
+  }
+
+  if ((options & WUFFS_INITIALIZE__ALREADY_ZEROED) != 0) {
+    // The whole point of this if-check is to detect an uninitialized *self.
+    // We disable the warning on GCC. Clang-5.0 does not have this warning.
+#if !defined(__clang__) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+    if (self->private_impl.magic != 0) {
+      return wuffs_base__make_status(wuffs_base__error__initialize_falsely_claimed_already_zeroed);
+    }
+#if !defined(__clang__) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+  } else {
+    if ((options & WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED) == 0) {
+      memset(self, 0, sizeof(*self));
+      options |= WUFFS_INITIALIZE__ALREADY_ZEROED;
+    } else {
+      memset(&(self->private_impl), 0, sizeof(self->private_impl));
+    }
+  }
+
+  self->private_impl.magic = WUFFS_BASE__MAGIC;
+  self->private_impl.vtable_for__wuffs_base__token_decoder.vtable_name =
+      wuffs_base__token_decoder__vtable_name;
+  self->private_impl.vtable_for__wuffs_base__token_decoder.function_pointers =
+      (const void*)(&wuffs_cbor__decoder__func_ptrs_for__wuffs_base__token_decoder);
+  return wuffs_base__make_status(NULL);
+}
+
+wuffs_cbor__decoder*
+wuffs_cbor__decoder__alloc() {
+  wuffs_cbor__decoder* x =
+      (wuffs_cbor__decoder*)(calloc(sizeof(wuffs_cbor__decoder), 1));
+  if (!x) {
+    return NULL;
+  }
+  if (wuffs_cbor__decoder__initialize(
+      x, sizeof(wuffs_cbor__decoder), WUFFS_VERSION, WUFFS_INITIALIZE__ALREADY_ZEROED).repr) {
+    free(x);
+    return NULL;
+  }
+  return x;
+}
+
+size_t
+sizeof__wuffs_cbor__decoder() {
+  return sizeof(wuffs_cbor__decoder);
+}
+
+// ---------------- Function Implementations
+
+// -------- func cbor.decoder.set_quirk_enabled
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct
+wuffs_cbor__decoder__set_quirk_enabled(
+    wuffs_cbor__decoder* self,
+    uint32_t a_quirk,
+    bool a_enabled) {
+  return wuffs_base__make_empty_struct();
+}
+
+// -------- func cbor.decoder.workbuf_len
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__range_ii_u64
+wuffs_cbor__decoder__workbuf_len(
+    const wuffs_cbor__decoder* self) {
+  if (!self) {
+    return wuffs_base__utility__empty_range_ii_u64();
+  }
+  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
+      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
+    return wuffs_base__utility__empty_range_ii_u64();
+  }
+
+  return wuffs_base__utility__empty_range_ii_u64();
+}
+
+// -------- func cbor.decoder.decode_tokens
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status
+wuffs_cbor__decoder__decode_tokens(
+    wuffs_cbor__decoder* self,
+    wuffs_base__token_buffer* a_dst,
+    wuffs_base__io_buffer* a_src,
+    wuffs_base__slice_u8 a_workbuf) {
+  if (!self) {
+    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
+  }
+  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
+    return wuffs_base__make_status(
+        (self->private_impl.magic == WUFFS_BASE__DISABLED)
+        ? wuffs_base__error__disabled_by_previous_error
+        : wuffs_base__error__initialize_not_called);
+  }
+  if (!a_dst || !a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+  if ((self->private_impl.active_coroutine != 0) &&
+      (self->private_impl.active_coroutine != 1)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__make_status(wuffs_base__error__interleaved_coroutine_calls);
+  }
+  self->private_impl.active_coroutine = 0;
+  wuffs_base__status status = wuffs_base__make_status(NULL);
+
+  uint32_t v_depth = 0;
+  uint32_t v_token_length = 0;
+  uint8_t v_c = 0;
+
+  wuffs_base__token* iop_a_dst = NULL;
+  wuffs_base__token* io0_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  wuffs_base__token* io1_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  wuffs_base__token* io2_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_dst) {
+    io0_a_dst = a_dst->data.ptr;
+    io1_a_dst = io0_a_dst + a_dst->meta.wi;
+    iop_a_dst = io1_a_dst;
+    io2_a_dst = io0_a_dst + a_dst->data.len;
+    if (a_dst->meta.closed) {
+      io2_a_dst = iop_a_dst;
+    }
+  }
+  const uint8_t* iop_a_src = NULL;
+  const uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  const uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  const uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
+  }
+
+  uint32_t coro_susp_point = self->private_impl.p_decode_tokens[0];
+  if (coro_susp_point) {
+    v_depth = self->private_data.s_decode_tokens[0].v_depth;
+    v_token_length = self->private_data.s_decode_tokens[0].v_token_length;
+    v_c = self->private_data.s_decode_tokens[0].v_c;
+  }
+  switch (coro_susp_point) {
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+
+    if (self->private_impl.f_end_of_data) {
+      status = wuffs_base__make_status(wuffs_base__note__end_of_data);
+      goto ok;
+    }
+    label__outer__continue:;
+    while (true) {
+      while (true) {
+        if (((uint64_t)(io2_a_dst - iop_a_dst)) <= 0) {
+          status = wuffs_base__make_status(wuffs_base__suspension__short_write);
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(1);
+          goto label__outer__continue;
+        }
+        if (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
+          if (a_src && a_src->meta.closed) {
+            status = wuffs_base__make_status(wuffs_cbor__error__bad_input);
+            goto exit;
+          }
+          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(2);
+          goto label__outer__continue;
+        }
+        v_c = wuffs_base__load_u8be__no_bounds_check(iop_a_src);
+        if ((24 <= (v_c & 31)) && ((v_c & 31) <= 27)) {
+          v_token_length = (1 + (((uint32_t)(1)) << (v_c & 3)));
+          if (((uint64_t)(io2_a_src - iop_a_src)) < ((uint64_t)(v_token_length))) {
+            if (a_src && a_src->meta.closed) {
+              status = wuffs_base__make_status(wuffs_cbor__error__bad_input);
+              goto exit;
+            }
+            status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(3);
+            goto label__outer__continue;
+          }
+          self->private_data.s_decode_tokens[0].scratch = v_token_length;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+          if (self->private_data.s_decode_tokens[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
+            self->private_data.s_decode_tokens[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
+            iop_a_src = io2_a_src;
+            status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+            goto suspend;
+          }
+          iop_a_src += self->private_data.s_decode_tokens[0].scratch;
+        } else {
+          (iop_a_src += 1, wuffs_base__make_empty_struct());
+        }
+        if (v_c < 32) {
+          v_c &= 31;
+          if (v_c < 24) {
+            *iop_a_dst++ = wuffs_base__make_token(
+                (((uint64_t)((12582912 | ((uint32_t)(v_c))))) << WUFFS_BASE__TOKEN__VALUE_MINOR__SHIFT) |
+                (((uint64_t)(1)) << WUFFS_BASE__TOKEN__LENGTH__SHIFT));
+            goto label__goto_parsed_a_leaf_value__break;
+          } else if (v_c < 28) {
+            *iop_a_dst++ = wuffs_base__make_token(
+                (((uint64_t)(10490116)) << WUFFS_BASE__TOKEN__VALUE_MINOR__SHIFT) |
+                (((uint64_t)((1 + (((uint32_t)(1)) << (v_c - 24))))) << WUFFS_BASE__TOKEN__LENGTH__SHIFT));
+            goto label__goto_parsed_a_leaf_value__break;
+          }
+        }
+        if (iop_a_src > io1_a_src) {
+          (iop_a_src--, wuffs_base__make_empty_struct());
+          status = wuffs_base__make_status(wuffs_cbor__error__bad_input);
+          goto exit;
+        }
+        status = wuffs_base__make_status(wuffs_cbor__error__internal_error_inconsistent_i_o);
+        goto exit;
+      }
+      label__goto_parsed_a_leaf_value__break:;
+      if (v_depth == 0) {
+        goto label__outer__break;
+      }
+    }
+    label__outer__break:;
+    self->private_impl.f_end_of_data = true;
+
+    goto ok;
+    ok:
+    self->private_impl.p_decode_tokens[0] = 0;
+    goto exit;
+  }
+
+  goto suspend;
+  suspend:
+  self->private_impl.p_decode_tokens[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine = wuffs_base__status__is_suspension(&status) ? 1 : 0;
+  self->private_data.s_decode_tokens[0].v_depth = v_depth;
+  self->private_data.s_decode_tokens[0].v_token_length = v_token_length;
+  self->private_data.s_decode_tokens[0].v_c = v_c;
+
+  goto exit;
+  exit:
+  if (a_dst) {
+    a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
+  }
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+  }
+
+  if (wuffs_base__status__is_error(&status)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+  }
+  return status;
+}
+
+#endif  // !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__CBOR)
+
 #if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__CRC32)
 
 // ---------------- Status Codes Implementations
diff --git a/script/print-json-token-debug-format.c b/script/print-json-token-debug-format.c
index a2a5277..7c306e8 100644
--- a/script/print-json-token-debug-format.c
+++ b/script/print-json-token-debug-format.c
@@ -82,6 +82,7 @@
 // code simply isn't compiled.
 #define WUFFS_CONFIG__MODULES
 #define WUFFS_CONFIG__MODULE__BASE
+#define WUFFS_CONFIG__MODULE__CBOR
 #define WUFFS_CONFIG__MODULE__JSON
 
 // If building this program in an environment that doesn't easily accommodate
@@ -91,8 +92,14 @@
 
 // Wuffs allows either statically or dynamically allocated work buffers. This
 // program exercises static allocation.
+#if WUFFS_CBOR__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE > \
+    WUFFS_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE
+#define WORK_BUFFER_ARRAY_SIZE \
+  WUFFS_CBOR__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE
+#else
 #define WORK_BUFFER_ARRAY_SIZE \
   WUFFS_JSON__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE
+#endif
 #if WORK_BUFFER_ARRAY_SIZE > 0
 uint8_t g_work_buffer_array[WORK_BUFFER_ARRAY_SIZE];
 #else
@@ -113,7 +120,9 @@
 wuffs_base__io_buffer g_src;
 wuffs_base__token_buffer g_tok;
 
-wuffs_json__decoder g_dec;
+wuffs_cbor__decoder g_cbor_decoder;
+wuffs_json__decoder g_json_decoder;
+wuffs_base__token_decoder* g_dec;
 wuffs_base__status g_dec_status;
 
 #define TRY(error_msg)         \
@@ -149,12 +158,18 @@
 
 // ----
 
+typedef enum file_format_enum {
+  FILE_FORMAT_JSON,
+  FILE_FORMAT_CBOR,
+} file_format;
+
 struct {
   int remaining_argc;
   char** remaining_argv;
 
   bool all_tokens;
   bool human_readable;
+  file_format input_format;
   bool quirks;
 } g_flags = {0};
 
@@ -188,6 +203,14 @@
       g_flags.human_readable = true;
       continue;
     }
+    if (!strcmp(arg, "i=cbor") || !strcmp(arg, "input-format=cbor")) {
+      g_flags.input_format = FILE_FORMAT_CBOR;
+      continue;
+    }
+    if (!strcmp(arg, "i=json") || !strcmp(arg, "input-format=json")) {
+      g_flags.input_format = FILE_FORMAT_JSON;
+      continue;
+    }
     if (!strcmp(arg, "q") || !strcmp(arg, "quirks")) {
       g_flags.quirks = true;
       continue;
@@ -208,7 +231,7 @@
     "3:UnicodeCodePoint",  //
     "4:Literal.........",  //
     "5:Number..........",  //
-    "6:Reserved........",  //
+    "6:Inline_Integer..",  //
     "7:Reserved........",  //
     "8:Reserved........",  //
     "9:Reserved........",  //
@@ -241,10 +264,22 @@
       wuffs_base__make_slice_token(g_tok_buffer_array, TOKEN_BUFFER_ARRAY_SIZE),
       wuffs_base__empty_token_buffer_meta());
 
-  wuffs_base__status init_status = wuffs_json__decoder__initialize(
-      &g_dec, sizeof__wuffs_json__decoder(), WUFFS_VERSION, 0);
-  if (!wuffs_base__status__is_ok(&init_status)) {
-    return wuffs_base__status__message(&init_status);
+  if (g_flags.input_format == FILE_FORMAT_JSON) {
+    wuffs_base__status init_status = wuffs_json__decoder__initialize(
+        &g_json_decoder, sizeof__wuffs_json__decoder(), WUFFS_VERSION, 0);
+    if (!wuffs_base__status__is_ok(&init_status)) {
+      return wuffs_base__status__message(&init_status);
+    }
+    g_dec = wuffs_json__decoder__upcast_as__wuffs_base__token_decoder(
+        &g_json_decoder);
+  } else {
+    wuffs_base__status init_status = wuffs_cbor__decoder__initialize(
+        &g_cbor_decoder, sizeof__wuffs_cbor__decoder(), WUFFS_VERSION, 0);
+    if (!wuffs_base__status__is_ok(&init_status)) {
+      return wuffs_base__status__message(&init_status);
+    }
+    g_dec = wuffs_cbor__decoder__upcast_as__wuffs_base__token_decoder(
+        &g_cbor_decoder);
   }
 
   if (g_flags.quirks) {
@@ -269,14 +304,14 @@
     };
     uint32_t i;
     for (i = 0; quirks[i]; i++) {
-      wuffs_json__decoder__set_quirk_enabled(&g_dec, quirks[i], true);
+      wuffs_base__token_decoder__set_quirk_enabled(g_dec, quirks[i], true);
     }
   }
 
   uint64_t pos = 0;
   while (true) {
-    wuffs_base__status status = wuffs_json__decoder__decode_tokens(
-        &g_dec, &g_tok, &g_src,
+    wuffs_base__status status = wuffs_base__token_decoder__decode_tokens(
+        g_dec, &g_tok, &g_src,
         wuffs_base__make_slice_u8(g_work_buffer_array, WORK_BUFFER_ARRAY_SIZE));
 
     while (g_tok.meta.ri < g_tok.meta.wi) {
diff --git a/std/cbor/decode_cbor.wuffs b/std/cbor/decode_cbor.wuffs
new file mode 100644
index 0000000..7ec6084
--- /dev/null
+++ b/std/cbor/decode_cbor.wuffs
@@ -0,0 +1,108 @@
+// Copyright 2020 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+pub status "#bad input"
+
+pri status "#internal error: inconsistent I/O"
+
+pub struct decoder? implements base.token_decoder(
+	end_of_data : base.bool,
+
+	util : base.utility,
+)(
+)
+
+pub func decoder.set_quirk_enabled!(quirk: base.u32, enabled: base.bool) {
+}
+
+pub func decoder.workbuf_len() base.range_ii_u64 {
+	return this.util.empty_range_ii_u64()
+}
+
+pub func decoder.decode_tokens?(dst: base.token_writer, src: base.io_reader, workbuf: slice base.u8) {
+	var depth        : base.u32[..= 1024]
+	var token_length : base.u32[..= 9]
+	var c            : base.u8
+
+	if this.end_of_data {
+		return base."@end of data"
+	}
+
+	while.outer true {
+		while.goto_parsed_a_leaf_value true {{
+		if args.dst.available() <= 0 {
+			yield? base."$short write"
+			continue.outer
+		}
+		if args.src.available() <= 0 {
+			if args.src.is_closed() {
+				return "#bad input"
+			}
+			yield? base."$short read"
+			continue.outer
+		}
+		c = args.src.peek_u8()
+		if (0x18 <= (c & 0x1F)) and ((c & 0x1F) <= 0x1B) {
+			token_length = 1 + ((1 as base.u32) << (c & 0x03))
+			if args.src.available() < (token_length as base.u64) {
+				if args.src.is_closed() {
+					return "#bad input"
+				}
+				yield? base."$short read"
+				continue.outer
+			}
+			args.src.skip_u32?(n: token_length)
+		} else {
+			args.src.skip_u32_fast!(actual: 1, worst_case: 1)
+		}
+
+		if c < 0x20 {
+			c &= 0x1F
+			if c < 0x18 {
+				args.dst.write_simple_token_fast!(
+					value_major: 0,
+					value_minor: (base.TOKEN__VBC__INLINE_INTEGER << 21) |
+					(c as base.u32),
+					continued: 0,
+					length: 1)
+				break.goto_parsed_a_leaf_value
+			} else if c < 0x1C {
+				args.dst.write_simple_token_fast!(
+					value_major: 0,
+					value_minor: (base.TOKEN__VBC__NUMBER << 21) |
+					base.TOKEN__VBD__NUMBER__CONTENT_INTEGER_UNSIGNED |
+					base.TOKEN__VBD__NUMBER__FORMAT_BINARY_BIG_ENDIAN |
+					base.TOKEN__VBD__NUMBER__FORMAT_IGNORE_FIRST_BYTE,
+					continued: 0,
+					length: 1 + ((1 as base.u32) << (c - 0x18)))
+				break.goto_parsed_a_leaf_value
+			}
+		}
+
+		if args.src.can_undo_byte() {
+			args.src.undo_byte!()
+			return "#bad input"
+		}
+		return "#internal error: inconsistent I/O"
+		}} endwhile.goto_parsed_a_leaf_value
+
+		// We've just parsed a leaf (non-container) value: literal (null,
+		// false, true), number or string.
+		if depth == 0 {
+			break.outer
+		}
+	} endwhile.outer
+
+	this.end_of_data = true
+}
diff --git a/test/c/std/cbor.c b/test/c/std/cbor.c
new file mode 100644
index 0000000..e903a63
--- /dev/null
+++ b/test/c/std/cbor.c
@@ -0,0 +1,133 @@
+// Copyright 2020 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// ----------------
+
+/*
+This test program is typically run indirectly, by the "wuffs test" or "wuffs
+bench" commands. These commands take an optional "-mimic" flag to check that
+Wuffs' output mimics (i.e. exactly matches) other libraries' output, such as
+giflib for GIF, libpng for PNG, etc.
+
+To manually run this test:
+
+for CC in clang gcc; do
+  $CC -std=c99 -Wall -Werror cbor.c && ./a.out
+  rm -f a.out
+done
+
+Each edition should print "PASS", amongst other information, and exit(0).
+
+Add the "wuffs mimic cflags" (everything after the colon below) to the C
+compiler flags (after the .c file) to run the mimic tests.
+
+To manually run the benchmarks, replace "-Wall -Werror" with "-O3" and replace
+the first "./a.out" with "./a.out -bench". Combine these changes with the
+"wuffs mimic cflags" to run the mimic benchmarks.
+*/
+
+// !! wuffs mimic cflags: -DWUFFS_MIMIC
+
+// Wuffs ships as a "single file C library" or "header file library" as per
+// https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
+//
+// To use that single file as a "foo.c"-like implementation, instead of a
+// "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or
+// compiling it.
+#define WUFFS_IMPLEMENTATION
+
+// Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
+// release/c/etc.c whitelist which parts of Wuffs to build. That file contains
+// the entire Wuffs standard library, implementing a variety of codecs and file
+// formats. Without this macro definition, an optimizing compiler or linker may
+// very well discard Wuffs code for unused codecs, but listing the Wuffs
+// modules we use makes that process explicit. Preprocessing means that such
+// code simply isn't compiled.
+#define WUFFS_CONFIG__MODULES
+#define WUFFS_CONFIG__MODULE__BASE
+#define WUFFS_CONFIG__MODULE__CBOR
+
+// If building this program in an environment that doesn't easily accommodate
+// relative includes, you can use the script/inline-c-relative-includes.go
+// program to generate a stand-alone C file.
+#include "../../../release/c/wuffs-unsupported-snapshot.c"
+#include "../testlib/testlib.c"
+#ifdef WUFFS_MIMIC
+// No mimic library.
+#endif
+
+// ---------------- CBOR Tests
+
+const char*  //
+test_wuffs_cbor_decode_interface() {
+  CHECK_FOCUS(__func__);
+
+  // TODO.
+
+  return NULL;
+}
+
+// ---------------- Mimic Tests
+
+#ifdef WUFFS_MIMIC
+
+// No mimic tests.
+
+#endif  // WUFFS_MIMIC
+
+// ---------------- CBOR Benches
+
+// No CBOR benches.
+
+// ---------------- Mimic Benches
+
+#ifdef WUFFS_MIMIC
+
+// No mimic benches.
+
+#endif  // WUFFS_MIMIC
+
+// ---------------- Manifest
+
+proc g_tests[] = {
+
+    test_wuffs_cbor_decode_interface,
+
+#ifdef WUFFS_MIMIC
+
+// No mimic tests.
+
+#endif  // WUFFS_MIMIC
+
+    NULL,
+};
+
+proc g_benches[] = {
+
+// No CBOR benches.
+
+#ifdef WUFFS_MIMIC
+
+// No mimic benches.
+
+#endif  // WUFFS_MIMIC
+
+    NULL,
+};
+
+int  //
+main(int argc, char** argv) {
+  g_proc_package_name = "std/cbor";
+  return test_main(argc, argv, g_tests, g_benches);
+}