Squashed 'third_party/flatbuffers/' changes from e5f331db9..bc44fad35

bc44fad35 UnPackTo disable merge by default (#7527)
4fca4dc60 [TS/JS] Move TS tests to dedicated folder and deps upgrade (#7508)
036032373 Bump junit from 4.13 to 4.13.1 in /java (#7526)
89dfb43f3 Replace `bash JavaTest.sh` with `mvn test` (#7500)
c49aff4b6 enabled cpp17 tests in CI (#7524)
56e60223c prevent force_align attribute on enums (#7523)
89b1f5aa1 remove travis config (#7522)
b90159823 [Java][Flexbuffers] Add API to add nullables into the buffer. (#7521)
8cdc6a288 Install BuildFlatBuffers.cmake (#7519)
a67e35aff Moves all of the swift test code into tests/swift (#7509)
f124e41ae Updated Readme
4c954181c [Java][FlexBuffers] throwing exception for untyped fixed vectors (#7507)
7f7547737 [Android] Remove maven dependency of flatbuffers and use source folder (#7503)
a79d61ea8 Fixes issue with cocoapods failing to be published because of docc (#7505)
d465b39c3 [CMake]: fix breaking find_package change (#7499) (#7502)
c5a609dc2 [C#] Prepares for official Nuget release (#7496)
5634dc3d0 [ISSUE-6268] returns NaN insteadof nan (#7498)
37e37b8ca Updates cocoapods version (#7497)
8fd4534fb update android multidex setting (#7495)
d5427da52 Disable Android Build (#7494)
06c5c7ed0 FlatBuffers Version 2.0.8 (#7492)
b190ce11b Verifier Refinements (#7490)
bf5d23230 Namer applied to Typescript generator (#7488)
ce382d6dd [TS/JS] Add rollup and config to generate iife flatbuffers bundle (#7449)
41d9add7e C++: Add option to skip verifying nested flatbuffers (#7489)
6a8742754 [C++] support native_inline attribute for vector of tables (#7479)
694add668 Refactor test.cpp (#7487)
7edf8c908 Update scorecard to 1.1.2
b86387442 Fix typos (#7483)
e2eb5ee67 Include <array> head in stl_emulation.h (#7480)
994502b6d Version number in file package.json updated to 2.0.7 (#7476)
fa41e8367 [C++] Fixed crash when copying table with empty shared strings (#7477)
799cc8f7b Use type traits for specialization (#7475)
b7eb44147 Disable RTTI and rework use in idl_gen_ts.cpp (#7474)
8d01c5859 CMake project version detection made more robust (#7473)
237e8b71f Moved compiler warnings around (#7471)
eeb8fd60d Include builder.addOffset for vector of structs (#7470)
fef2ffc4d Use schema include name for keep-prefix (#7469)
8367664f1 Flatbuffers Version 2.0.7 (#7462)
d6f06c33f Reworked keep prefix (#7456)
627e8bf36 update grpc version (#7457)
883c42b7d disabling unpackto optimization (#7459)
7aae0af30 Remove old GRPC bash script and convert to python3 (#7454)
b057aa917 Grouped anonymous namespaces together, (#7455)
f1b26ff7f Change to GetTypeName (#7453)
9610a666b Generate SLSA signatures for Released zip files (#7450)
1e0f75a64 [WIP] speedup (#7452)
82b75407a Wrap types in namespace for --ts-flat-files and --gen-all (#7451)
f7c511957 Audit and fixups for GCC and Clang (#7212)
a66de58af Partial support for --ts-flat-files and --gen-all (#7446)
a3508f36d [Kotlin] Make sure namespace path exist for code generation (#7357)
137fec716 Stop using __has_trivial_copy on recent clang versions. (#7443)
214125e41 [C#] Rework how sorted vectors are looked up (#7441)
44a7dc999 Define minimum buffer size (#7440)
3cc2daa78 make_span overloads for pointer to vector (#7374) (#7435)
fa1174aa7 [TypeScript] Fix namespaceless schema generation (#7432)
83d4e2a10 Add checks to verifier (#7438)
8a09f3fb0 Fix FlexBuffers JS/TS bug https://github.com/google/flatbuffers/issues/6934 (#7434)
9dbe819ef Add flatc python tests to CI (#7437)
67c414958 Update TypeScriptTest.py to work better cross platform (#7436)
8b8c7dbdf Update gitingore to reflect name change (#7431)
2ee20a5f3 Remove auto including locale functions (#7430)
4be605604 [C++] Set StructDef::has_key property when deserializing from binary schema (#7386) (#7428)
fc5d86f1e [C++] Make template parameter in stl_emulation.h more explicit to avoid conflicts with cpprestsdk U macro (#7424)
9dce287ad Issue#6959 :Updated Automatically generated rust files. (#7425)
7798be3bb avoid zero-as-null-pointer warning (#7423)
966362e07 [C++] Vector of Tables equality (#7415)
a89c279ed [golang] Perform keyword escaping after case conversion (#7421)
a212b3c03 Turn of fail fast for C++ CI
9230f600d Remove stringop-overflow from error (#7422)
c79362156 [golang] Add support for text parsing with json struct tags (#7353)
ee2ced236 Moved TypeScriptTests to python script (#7411)
468c00a3f Rebased: grpc/compiler: Respect filename suffix and extension during code generation (#7414)
47c757f71 Add tests for flatc (#7405)
9a5ff8900 Add FLATBUFFERS_STRICT_MODE (#7408)
950444a34 [TS] Use TextEncoder and TextDecoder (#7400)
30d76198c Compilation issue msys2 #7399 (#7409)
cce3a66f0 Delete .travis directory
8d1cc6ac7 Revert "Compilation issue msys2 (#7403)" (#7407)
5b207639a Update readme.md
359e0f9d6 Revert "grpc/compiler: Respect filename suffix and extension during code generation (#7343)" (#7406)
ebbed0513 Delete cpp-linter.yml
aa395e5a5 (#7323) Rename CMake files according to project name (#7378)
32328075d Fix error msg format when generate GRPC failed (#7350)
97e89c5ac grpc/compiler: Respect filename suffix and extension during code generation (#7343)
5f6672be4 Fix Clang-Cl compile on Windows (#7308)
28e858c85 [TS/Bazel] Minor improvements to typescript.bzl (#7300)
987bebe67 [TS] fix incorrect reverse when writting array of structs (#7271)
ec0129369 Fix FlexBuffers Verifier tracking vectors reuse at wrong offset
50dd385b3 Add missing const (#7401)
da702cfd8 Compilation issue msys2 (#7403)
6e2791640 keep-prefix keeps relative pathing (#7394)
52fce5e53 fix(#7360): grpc used deprecated functions (#7361)
b7f13cd8e cpp_generator: comment out unused parameter to avoid warnings (#7381)
e42985e5a Updated Newtonsoft.Json to 13.0.1 (#7393)
0a8064637 Fix references to LICENSE file (#7377)
b9eea76a8 [Dart] Implement putBool to fix errors when serializing structs with bools (#7359)
1b9030015 Bump Newtonsoft.Json from 12.0.3 to 13.0.1 in /tests/FlatBuffers.Test (#7363)
83a43fc79 Reenable optional json (#7352)
5f0137602 Only include direct included filed (#7348)
9a1913a87 Revert "Implement optional scalars for JSON (#7322)" (#7351)
b4647beb8 Revert "Move reflection_ts_fbs into a separate directory (#7342)" (#7349)
d6060977a Remove asserting in verifier for flattests
987aa5b5e move -Wextra-semi to GCC 8.0+
42acdb63c [TS] Don't generate self-imports with --ts-flat-file (#7340)
0cc1aeb8c [golang] Create missing namespace directory structure (#7324) (#7325)
ba6c67170 [Kotlin] Remove download benchmark files dependency (#7314)
d2f33fc45 Disable Android on Linux CI build
0d1b72cbc [TS] fix ts import path issue (#7298)
9fce2fbf2 replace io/ioutil to os (#7281)
a18ea40d6 Implement optional scalars for JSON (#7322)
090caa280 Move reflection_ts_fbs into a separate directory (#7342)
49e1ea333 Implement optional scalars for Python (#7318)
11a198870 Started implementation for private flags in rust (#7269)
967df08b1 Adds full supposed for Wasm in the swift lib (#7328)
9aa08a429 Use keep case for Rust union discriminant type. (#7321)
9e8c758f5 Add explicit return types to lobster generated code (#7312)
74a25536b Add size check to fix out of bounds read risk (#7304)
12917af8a Update Rust docs page (#7296)
1ea2472f7 [swift] add had<ArrayName> property for arrays to check presence in a message (#7280)
0fe13cb28 Remove span ConstIterator/cbegin()/cend(). (#7295)
385dddc66 Namerkot (#7245)
750dde766 Make `flatc` generate Rust files not requiring `std` (#7273)
9917a168c [swift] Make swift module public (#7274)
76d3cca19 Rust: fix a name conflict when building with "no_std" feature (#7268)
c86e6d0e3 json inf parsing
d34dc32c2 fix include order
234d86c92 fixed off-by-one in parser
746c73b91 Add Annotations for Monster schema and example buffer
0bbfd4b2e fixes for annotator
716521953 Update readme.md (#7257)
a45f564cf [performance] Add aggressive systematic inlining in ByteBuffer and FlatBufferBuilder (#7253)
9d45a6403 more google merge fixes
ccfb4c20b Handle +/-inf in protos (#7256)
7bcd857b8 Specialize CreateVector with std::initializer_list (#7254)
23c8ab34c Swift update performance benchmark infrastructure (#7255)
70002dc5c various fixes for google merge
6e0e79f24 Add test for nested buffer verifier (#7252)
b856368d7 Turn off go modules temporary until we get a proper fix (#7251)
e37156a30 Keep the underlying storage capacity when clearing the FlatBufferBuilder. Gives a significant performance boost for serialisation of many small messages. (#7250)
a10b0e546 Java namer variable keep case (#7249)
275b73994 allow overriding FLATBUFFERS_MAX_ALIGNMENT
9d1ce9a10 Add parameter back to EndVector (#7246)
79afe6c3d Make Java namespaces keep case by default (#7243)
c6dbb2230 Add write permissions for labeller
18bacd3ea Expand test to make sure {}-initializers are properly understood by template. (#7242)
a2c913aec Add -Wnon-virtual-dtor
67b33b294 set workflows permissions to read-only (#7239)
7b5fd2bd0 [Kotlin] Fix key lookup returning null clashing with default value (#7237)
7181d7770 [Java] Fix key lookup returning null clashing with default value (#7236)
7f663b120 Allow CreateVectorOfStrings() to work with any string-type. (#7238)
173ebb694 Fixes a bug where the create function doesnt optional + required items (#7228)
d65823948 [Kotlin] Update gradle to 7.4.1 and simplify config files. (#7231)
ab4bf59e8 remove toascii (#7234)
eee44bbb2 disable cpp-linter (#7229)
a63fa51a1 Create cpp-linter.yml (#7208)
2049e5210 Adds a way to verify/exposes Entities ids (#7221)
832c618f5 Adds implementation flag for swift (#7202)
14615699f Started to migrate to target_compile_options (#7222)
20aad0c41 [C++] stl_emulation span::count_ is not const anymore (#7226) (#7227)
f083b33f2 code gen flexbuffer verifier (#7207)
bf17df346 [C++] generate sorted #include directives (#7213)
35281dedb Fix for [C++] flatc generates invalid Code in the default constructor for structs, when --cpp-field-case-style is used #7209 (#7211)
c9651b742 Add overloads for C# ByteBuffer/FlatBufferBuilder to allow adding vector blocks from ArraySegments or IntPtr (#7193)
26c3b3ada Update codeql.yml
da6e1b985 Update codeql.yml
ad27d751e Added Oss fuzz badge
0aab623cb Create codeql.yml
6a446bdd8 maximize parallel builds in CI (#7206)
21fb5cbbc Create scorecards.yml
0da6f9486 [C++] Static assert on Flatbuffers Version (#7203)
59e971308 reduce fuzzing time to 1 minute in CI
40866a892 fixed padding in struct for annotated binary (#7199)
b71d968fa Apply Namer prefix/suffix to other generators (#7197)
fac0d7be0 Apply Namer to Java. (#7194)
6c5603fd9 [C#] Fix collision of field name and type name (#7149)
2d21853a7 monster fuzzer fix for json default scalars
fec1a8d01 [swift] Add bazel configuration for Swift (#7195)
7fd857623 structured comments (#7192)
a4cb1599d Namerdart (#7187)
ae4ce7265 fuzzed binary annotator (#7188)
e2be0c0b0 Handle root offset and root table vtable invalidation (#7177)
2ad408697 [TS] Fix generation of struct members in object api (#7148)
4213d9105 VerifySizePrefixed (reflection::Schema) and GetAnySizePrefixedRoot added (#7181)
5a13f622c Correctly parse lists of enums in Dart generated code (#7157)
23a7e4e0b Adds no-includes flags to the swift code generator (#7182)
eeb49c275 Move flatbuffer_ts_library to typescript.bzl (#7183)
824763b31 Typo in flatc options (warning-as-errors instead of warnings-as-errors) (#7180)
d3aeee32b Annotated Flatbuffer Binary (#7174)
0bceba24d [Lua] Apply Namer to Lua (#7171)
b8c77d404 Make inclusion of header <optional> opt-out via macro (#7168)
8468eab83 Namersw (#7167)
2b2e8d4ae Nameroverloads (#7164)
b80b32bfa Use DESCRIPTION only if CMake version >= 3.9 (#7166)

Change-Id: Ic2681dabb1a798b7515e62753ee06aecb9933260
git-subtree-dir: third_party/flatbuffers
git-subtree-split: bc44fad35271e43fd7a79b4d691ac9e41708797f
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/src/BUILD.bazel b/src/BUILD.bazel
index d8bbfcd..b01551d 100644
--- a/src/BUILD.bazel
+++ b/src/BUILD.bazel
@@ -34,10 +34,16 @@
 cc_library(
     name = "flatc_library",
     srcs = [
+        "annotated_binary_text_gen.cpp",
+        "annotated_binary_text_gen.h",
         "bfbs_gen.h",
         "bfbs_gen_lua.cpp",
         "bfbs_gen_lua.h",
+        "bfbs_namer.h",
+        "binary_annotator.cpp",
+        "binary_annotator.h",
         "flatc.cpp",
+        "namer.h",
     ],
     hdrs = [
         "//:flatc_headers",
@@ -56,6 +62,7 @@
         "bfbs_gen.h",
         "bfbs_gen_lua.cpp",
         "bfbs_gen_lua.h",
+        "bfbs_namer.h",
         "flatc_main.cpp",
         "idl_gen_cpp.cpp",
         "idl_gen_csharp.cpp",
@@ -73,6 +80,7 @@
         "idl_gen_swift.cpp",
         "idl_gen_text.cpp",
         "idl_gen_ts.cpp",
+        "idl_namer.h",
         "namer.h",
         "util.cpp",
     ],
diff --git a/src/annotated_binary_text_gen.cpp b/src/annotated_binary_text_gen.cpp
new file mode 100644
index 0000000..ec30b1d
--- /dev/null
+++ b/src/annotated_binary_text_gen.cpp
@@ -0,0 +1,439 @@
+#include "annotated_binary_text_gen.h"
+
+#include <sstream>
+#include <string>
+
+#include "binary_annotator.h"
+#include "flatbuffers/base.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+namespace {
+
+struct OutputConfig {
+  size_t largest_type_string = 10;
+
+  size_t largest_value_string = 20;
+
+  size_t max_bytes_per_line = 8;
+
+  size_t offset_max_char = 4;
+
+  char delimiter = '|';
+};
+
+static std::string ToString(const BinarySectionType type) {
+  switch (type) {
+    case BinarySectionType::Header: return "header";
+    case BinarySectionType::Table: return "table";
+    case BinarySectionType::RootTable: return "root_table";
+    case BinarySectionType::VTable: return "vtable";
+    case BinarySectionType::Struct: return "struct";
+    case BinarySectionType::String: return "string";
+    case BinarySectionType::Vector: return "vector";
+    case BinarySectionType::Unknown: return "unknown";
+    case BinarySectionType::Union: return "union";
+    case BinarySectionType::Padding: return "padding";
+    default: return "todo";
+  }
+}
+
+static bool IsOffset(const BinaryRegionType type) {
+  return type == BinaryRegionType::UOffset || type == BinaryRegionType::SOffset;
+}
+
+template<typename T> std::string ToString(T value) {
+  if (std::is_floating_point<T>::value) {
+    std::stringstream ss;
+    ss << value;
+    return ss.str();
+  } else {
+    return std::to_string(value);
+  }
+}
+
+template<typename T>
+std::string ToValueString(const BinaryRegion &region, const uint8_t *binary) {
+  std::string s;
+  s += "0x";
+  const T val = ReadScalar<T>(binary + region.offset);
+  const uint64_t start_index = region.offset + region.length - 1;
+  for (uint64_t i = 0; i < region.length; ++i) {
+    s += ToHex(binary[start_index - i]);
+  }
+  s += " (";
+  s += ToString(val);
+  s += ")";
+  return s;
+}
+
+template<>
+std::string ToValueString<std::string>(const BinaryRegion &region,
+                                       const uint8_t *binary) {
+  return std::string(reinterpret_cast<const char *>(binary + region.offset),
+                     static_cast<size_t>(region.array_length));
+}
+
+static std::string ToValueString(const BinaryRegion &region,
+                                 const uint8_t *binary,
+                                 const OutputConfig &output_config) {
+  std::string s;
+
+  if (region.array_length) {
+    if (region.type == BinaryRegionType::Uint8 ||
+        region.type == BinaryRegionType::Unknown) {
+      // Interpet each value as a ASCII to aid debugging
+      for (uint64_t i = 0; i < region.array_length; ++i) {
+        const uint8_t c = *(binary + region.offset + i);
+        s += isprint(c) ? static_cast<char>(c & 0x7F) : '.';
+      }
+      return s;
+    } else if (region.type == BinaryRegionType::Char) {
+      // string value
+      return ToValueString<std::string>(region, binary);
+    }
+  }
+
+  switch (region.type) {
+    case BinaryRegionType::Uint32:
+      return ToValueString<uint32_t>(region, binary);
+    case BinaryRegionType::Int32: return ToValueString<int32_t>(region, binary);
+    case BinaryRegionType::Uint16:
+      return ToValueString<uint16_t>(region, binary);
+    case BinaryRegionType::Int16: return ToValueString<int16_t>(region, binary);
+    case BinaryRegionType::Bool: return ToValueString<bool>(region, binary);
+    case BinaryRegionType::Uint8: return ToValueString<uint8_t>(region, binary);
+    case BinaryRegionType::Char: return ToValueString<char>(region, binary);
+    case BinaryRegionType::Byte:
+    case BinaryRegionType::Int8: return ToValueString<int8_t>(region, binary);
+    case BinaryRegionType::Int64: return ToValueString<int64_t>(region, binary);
+    case BinaryRegionType::Uint64:
+      return ToValueString<uint64_t>(region, binary);
+    case BinaryRegionType::Double: return ToValueString<double>(region, binary);
+    case BinaryRegionType::Float: return ToValueString<float>(region, binary);
+    case BinaryRegionType::UType: return ToValueString<uint8_t>(region, binary);
+
+    // Handle Offsets separately, incase they add additional details.
+    case BinaryRegionType::UOffset:
+      s += ToValueString<uint32_t>(region, binary);
+      break;
+    case BinaryRegionType::SOffset:
+      s += ToValueString<int32_t>(region, binary);
+      break;
+    case BinaryRegionType::VOffset:
+      s += ToValueString<uint16_t>(region, binary);
+      break;
+
+    default: break;
+  }
+  // If this is an offset type, include the calculated offset location in the
+  // value.
+  // TODO(dbaileychess): It might be nicer to put this in the comment field.
+  if (IsOffset(region.type)) {
+    s += " Loc: +0x";
+    s += ToHex(region.points_to_offset, output_config.offset_max_char);
+  }
+  return s;
+}
+
+struct DocContinuation {
+  // The start column where the value text first starts
+  size_t value_start_column = 0;
+
+  // The remaining part of the doc to print.
+  std::string value;
+};
+
+static std::string GenerateTypeString(const BinaryRegion &region) {
+  return ToString(region.type) +
+         ((region.array_length)
+              ? "[" + std::to_string(region.array_length) + "]"
+              : "");
+}
+
+static std::string GenerateComment(const BinaryRegionComment &comment,
+                                   const BinarySection &) {
+  std::string s;
+  switch (comment.type) {
+    case BinaryRegionCommentType::Unknown: s = "unknown"; break;
+    case BinaryRegionCommentType::SizePrefix: s = "size prefix"; break;
+    case BinaryRegionCommentType::RootTableOffset:
+      s = "offset to root table `" + comment.name + "`";
+      break;
+    // TODO(dbaileychess): make this lowercase to follow the convention.
+    case BinaryRegionCommentType::FileIdentifier: s = "File Identifier"; break;
+    case BinaryRegionCommentType::Padding: s = "padding"; break;
+    case BinaryRegionCommentType::VTableSize: s = "size of this vtable"; break;
+    case BinaryRegionCommentType::VTableRefferingTableLength:
+      s = "size of referring table";
+      break;
+    case BinaryRegionCommentType::VTableFieldOffset:
+      s = "offset to field `" + comment.name;
+      break;
+    case BinaryRegionCommentType::VTableUnknownFieldOffset:
+      s = "offset to unknown field (id: " + std::to_string(comment.index) + ")";
+      break;
+
+    case BinaryRegionCommentType::TableVTableOffset:
+      s = "offset to vtable";
+      break;
+    case BinaryRegionCommentType::TableField:
+      s = "table field `" + comment.name;
+      break;
+    case BinaryRegionCommentType::TableUnknownField: s = "unknown field"; break;
+    case BinaryRegionCommentType::TableOffsetField:
+      s = "offset to field `" + comment.name + "`";
+      break;
+    case BinaryRegionCommentType::StructField:
+      s = "struct field `" + comment.name + "`";
+      break;
+    case BinaryRegionCommentType::ArrayField:
+      s = "array field `" + comment.name + "`[" +
+          std::to_string(comment.index) + "]";
+      break;
+    case BinaryRegionCommentType::StringLength: s = "length of string"; break;
+    case BinaryRegionCommentType::StringValue: s = "string literal"; break;
+    case BinaryRegionCommentType::StringTerminator:
+      s = "string terminator";
+      break;
+    case BinaryRegionCommentType::VectorLength:
+      s = "length of vector (# items)";
+      break;
+    case BinaryRegionCommentType::VectorValue:
+      s = "value[" + std::to_string(comment.index) + "]";
+      break;
+    case BinaryRegionCommentType::VectorTableValue:
+      s = "offset to table[" + std::to_string(comment.index) + "]";
+      break;
+    case BinaryRegionCommentType::VectorStringValue:
+      s = "offset to string[" + std::to_string(comment.index) + "]";
+      break;
+    case BinaryRegionCommentType::VectorUnionValue:
+      s = "offset to union[" + std::to_string(comment.index) + "]";
+      break;
+
+    default: break;
+  }
+  if (!comment.default_value.empty()) { s += " " + comment.default_value; }
+
+  switch (comment.status) {
+    case BinaryRegionStatus::OK: break;  // no-op
+    case BinaryRegionStatus::WARN: s = "WARN: " + s; break;
+    case BinaryRegionStatus::WARN_NO_REFERENCES:
+      s = "WARN: nothing refers to this section.";
+      break;
+    case BinaryRegionStatus::WARN_CORRUPTED_PADDING:
+      s = "WARN: could be corrupted padding region.";
+      break;
+    case BinaryRegionStatus::WARN_PADDING_LENGTH:
+      s = "WARN: padding is longer than expected.";
+      break;
+    case BinaryRegionStatus::ERROR: s = "ERROR: " + s; break;
+    case BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY:
+      s = "ERROR: " + s + ". Invalid offset, points outside the binary.";
+      break;
+    case BinaryRegionStatus::ERROR_INCOMPLETE_BINARY:
+      s = "ERROR: " + s + ". Incomplete binary, expected to read " +
+          comment.status_message + " bytes.";
+      break;
+    case BinaryRegionStatus::ERROR_LENGTH_TOO_LONG:
+      s = "ERROR: " + s + ". Longer than the binary.";
+      break;
+    case BinaryRegionStatus::ERROR_LENGTH_TOO_SHORT:
+      s = "ERROR: " + s + ". Shorter than the minimum length: ";
+      break;
+    case BinaryRegionStatus::ERROR_REQUIRED_FIELD_NOT_PRESENT:
+      s = "ERROR: " + s + ". Required field is not present.";
+      break;
+    case BinaryRegionStatus::ERROR_INVALID_UNION_TYPE:
+      s = "ERROR: " + s + ". Invalid union type value.";
+      break;
+    case BinaryRegionStatus::ERROR_CYCLE_DETECTED:
+      s = "ERROR: " + s + ". Invalid offset, cycle detected.";
+      break;
+  }
+
+  return s;
+}
+
+static std::string GenerateDocumentation(const BinaryRegion &region,
+                                         const BinarySection &section,
+                                         const uint8_t *binary,
+                                         DocContinuation &continuation,
+                                         const OutputConfig &output_config) {
+  std::string s;
+
+  // Check if there is a doc continuation that should be prioritized.
+  if (continuation.value_start_column) {
+    s += std::string(continuation.value_start_column - 2, ' ');
+    s += output_config.delimiter;
+    s += " ";
+
+    s += continuation.value.substr(0, output_config.max_bytes_per_line);
+    continuation.value = continuation.value.substr(
+        std::min(output_config.max_bytes_per_line, continuation.value.size()));
+    return s;
+  }
+
+  {
+    std::stringstream ss;
+    ss << std::setw(output_config.largest_type_string) << std::left;
+    ss << GenerateTypeString(region);
+    s += ss.str();
+  }
+  s += " ";
+  s += output_config.delimiter;
+  s += " ";
+  if (region.array_length) {
+    // Record where the value is first being outputted.
+    continuation.value_start_column = s.size();
+
+    // Get the full-length value, which we will chunk below.
+    const std::string value = ToValueString(region, binary, output_config);
+
+    std::stringstream ss;
+    ss << std::setw(output_config.largest_value_string) << std::left;
+    ss << value.substr(0, output_config.max_bytes_per_line);
+    s += ss.str();
+
+    continuation.value =
+        value.substr(std::min(output_config.max_bytes_per_line, value.size()));
+  } else {
+    std::stringstream ss;
+    ss << std::setw(output_config.largest_value_string) << std::left;
+    ss << ToValueString(region, binary, output_config);
+    s += ss.str();
+  }
+
+  s += " ";
+  s += output_config.delimiter;
+  s += " ";
+  s += GenerateComment(region.comment, section);
+
+  return s;
+}
+
+static std::string GenerateRegion(const BinaryRegion &region,
+                                  const BinarySection &section,
+                                  const uint8_t *binary,
+                                  const OutputConfig &output_config) {
+  std::string s;
+  bool doc_generated = false;
+  DocContinuation doc_continuation;
+  for (uint64_t i = 0; i < region.length; ++i) {
+    if ((i % output_config.max_bytes_per_line) == 0) {
+      // Start a new line of output
+      s += '\n';
+      s += "  ";
+      s += "+0x";
+      s += ToHex(region.offset + i, output_config.offset_max_char);
+      s += " ";
+      s += output_config.delimiter;
+    }
+
+    // Add each byte
+    s += " ";
+    s += ToHex(binary[region.offset + i]);
+
+    // Check for end of line or end of region conditions.
+    if (((i + 1) % output_config.max_bytes_per_line == 0) ||
+        i + 1 == region.length) {
+      if (i + 1 == region.length) {
+        // We are out of bytes but haven't the kMaxBytesPerLine, so we need to
+        // zero those out to align everything globally.
+        for (uint64_t j = i + 1; (j % output_config.max_bytes_per_line) != 0;
+             ++j) {
+          s += "   ";
+        }
+      }
+      s += " ";
+      s += output_config.delimiter;
+      // This is the end of the first line or its the last byte of the region,
+      // generate the end-of-line documentation.
+      if (!doc_generated) {
+        s += " ";
+        s += GenerateDocumentation(region, section, binary, doc_continuation,
+                                   output_config);
+
+        // If we have a value in the doc continuation, that means the doc is
+        // being printed on multiple lines.
+        doc_generated = doc_continuation.value.empty();
+      }
+    }
+  }
+
+  return s;
+}
+
+static std::string GenerateSection(const BinarySection &section,
+                                   const uint8_t *binary,
+                                   const OutputConfig &output_config) {
+  std::string s;
+  s += "\n";
+  s += ToString(section.type);
+  if (!section.name.empty()) { s += " (" + section.name + ")"; }
+  s += ":";
+  for (const BinaryRegion &region : section.regions) {
+    s += GenerateRegion(region, section, binary, output_config);
+  }
+  return s;
+}
+}  // namespace
+
+bool AnnotatedBinaryTextGenerator::Generate(
+    const std::string &filename, const std::string &schema_filename) {
+  OutputConfig output_config;
+  output_config.max_bytes_per_line = options_.max_bytes_per_line;
+
+  // Given the length of the binary, we can calculate the maximum number of
+  // characters to display in the offset hex: (i.e. 2 would lead to 0XFF being
+  // the max output).
+  output_config.offset_max_char =
+      binary_length_ > 0xFFFFFF
+          ? 8
+          : (binary_length_ > 0xFFFF ? 6 : (binary_length_ > 0xFF ? 4 : 2));
+
+  // Find the largest type string of all the regions in this file, so we can
+  // align the output nicely.
+  output_config.largest_type_string = 0;
+  for (const auto &section : annotations_) {
+    for (const auto &region : section.second.regions) {
+      std::string s = GenerateTypeString(region);
+      if (s.size() > output_config.largest_type_string) {
+        output_config.largest_type_string = s.size();
+      }
+
+      // Don't consider array regions, as they will be split to multiple lines.
+      if (!region.array_length) {
+        s = ToValueString(region, binary_, output_config);
+        if (s.size() > output_config.largest_value_string) {
+          output_config.largest_value_string = s.size();
+        }
+      }
+    }
+  }
+
+  // Generate each of the binary sections
+  std::string s;
+
+  s += "// Annotated Flatbuffer Binary\n";
+  s += "//\n";
+  s += "// Schema file: " + schema_filename + "\n";
+  s += "// Binary file: " + filename + "\n";
+
+  for (const auto &section : annotations_) {
+    s += GenerateSection(section.second, binary_, output_config);
+    s += "\n";
+  }
+
+  // Modify the output filename.
+  std::string output_filename = StripExtension(filename);
+  output_filename += options_.output_postfix;
+  output_filename +=
+      "." + (options_.output_extension.empty() ? GetExtension(filename)
+                                               : options_.output_extension);
+
+  return SaveFile(output_filename.c_str(), s, false);
+}
+
+}  // namespace flatbuffers
diff --git a/src/annotated_binary_text_gen.h b/src/annotated_binary_text_gen.h
new file mode 100644
index 0000000..712c452
--- /dev/null
+++ b/src/annotated_binary_text_gen.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ *     http://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.
+ */
+
+#ifndef FLATBUFFERS_ANNOTATED_BINARY_TEXT_GEN_H_
+#define FLATBUFFERS_ANNOTATED_BINARY_TEXT_GEN_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "binary_annotator.h"
+
+namespace flatbuffers {
+
+class AnnotatedBinaryTextGenerator {
+ public:
+  struct Options {
+    // The maximum number of raw bytes to print per line in the output. 8 is a
+    // good default due to the largest type (double) being 8 bytes long.
+    size_t max_bytes_per_line = 8;
+
+    // The output file postfix, appended between the filename and the extension.
+    // Example binary1.bin -> binary1_annotated.bin
+    std::string output_postfix = "";
+
+    // The output file extension, replacing any extension given. If empty, don't
+    // change the provided extension. AFB = Annotated Flatbuffer Binary
+    //
+    // Example: binary1.bin -> binary1.afb
+    std::string output_extension = "afb";
+  };
+
+  explicit AnnotatedBinaryTextGenerator(
+      const Options &options, std::map<uint64_t, BinarySection> annotations,
+      const uint8_t *const binary, const int64_t binary_length)
+      : annotations_(std::move(annotations)),
+        binary_(binary),
+        binary_length_(binary_length),
+        options_(options) {}
+
+  // Generate the annotated binary for the given `filename`. Returns true if the
+  // annotated binary was successfully saved.
+  bool Generate(const std::string &filename,
+                const std::string &schema_filename);
+
+ private:
+  const std::map<uint64_t, BinarySection> annotations_;
+
+  // The binary data itself.
+  const uint8_t *binary_;
+  const int64_t binary_length_;
+
+  // Output configuration
+  const Options options_;
+};
+
+}  // namespace flatbuffers
+
+#endif  // FLATBUFFERS_ANNOTATED_BINARY_TEXT_GEN_H_
diff --git a/src/bfbs_gen.h b/src/bfbs_gen.h
index 12aafb3..e706ece 100644
--- a/src/bfbs_gen.h
+++ b/src/bfbs_gen.h
@@ -24,19 +24,21 @@
 
 namespace flatbuffers {
 
-void ForAllEnums(
+namespace {
+
+static void ForAllEnums(
     const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
     std::function<void(const reflection::Enum *)> func) {
   for (auto it = enums->cbegin(); it != enums->cend(); ++it) { func(*it); }
 }
 
-void ForAllObjects(
+static void ForAllObjects(
     const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
     std::function<void(const reflection::Object *)> func) {
   for (auto it = objects->cbegin(); it != objects->cend(); ++it) { func(*it); }
 }
 
-void ForAllEnumValues(const reflection::Enum *enum_def,
+static void ForAllEnumValues(const reflection::Enum *enum_def,
                       std::function<void(const reflection::EnumVal *)> func) {
   for (auto it = enum_def->values()->cbegin(); it != enum_def->values()->cend();
        ++it) {
@@ -44,7 +46,7 @@
   }
 }
 
-void ForAllDocumentation(
+static void ForAllDocumentation(
     const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>
         *documentation,
     std::function<void(const flatbuffers::String *)> func) {
@@ -73,10 +75,6 @@
   return base_type == reflection::Obj;
 }
 
-static bool IsScalar(const reflection::BaseType base_type) {
-  return base_type >= reflection::UType && base_type <= reflection::Double;
-}
-
 static bool IsFloatingPoint(const reflection::BaseType base_type) {
   return base_type == reflection::Float || base_type == reflection::Double;
 }
@@ -93,21 +91,7 @@
   return base_type == reflection::Vector;
 }
 
-static std::string Denamespace(const flatbuffers::String *name,
-                               std::string &ns) {
-  const size_t pos = name->str().find_last_of('.');
-  if (pos == std::string::npos) {
-    ns = "";
-    return name->str();
-  }
-  ns = name->str().substr(0, pos);
-  return name->str().substr(pos + 1);
-}
-
-static std::string Denamespace(const flatbuffers::String *name) {
-  std::string ns;
-  return Denamespace(name, ns);
-}
+} // namespace
 
 // A concrete base Flatbuffer Generator that specific language generators can
 // derive from.
@@ -207,4 +191,4 @@
 
 }  // namespace flatbuffers
 
-#endif  // FLATBUFFERS_BFBS_GEN_H_
\ No newline at end of file
+#endif  // FLATBUFFERS_BFBS_GEN_H_
diff --git a/src/bfbs_gen_lua.cpp b/src/bfbs_gen_lua.cpp
index 868bb14..586f8ad 100644
--- a/src/bfbs_gen_lua.cpp
+++ b/src/bfbs_gen_lua.cpp
@@ -25,9 +25,11 @@
 
 // Ensure no includes to flatc internals. bfbs_gen.h and generator.h are OK.
 #include "bfbs_gen.h"
+#include "bfbs_namer.h"
 #include "flatbuffers/bfbs_generator.h"
 
 // The intermediate representation schema.
+#include "flatbuffers/reflection.h"
 #include "flatbuffers/reflection_generated.h"
 
 namespace flatbuffers {
@@ -36,6 +38,36 @@
 // To reduce typing
 namespace r = ::reflection;
 
+std::set<std::string> LuaKeywords() {
+  return { "and",   "break", "do",       "else", "elseif", "end",
+           "false", "for",   "function", "goto", "if",     "in",
+           "local", "nil",   "not",      "or",   "repeat", "return",
+           "then",  "true",  "until",    "while" };
+}
+
+Namer::Config LuaDefaultConfig() {
+  return { /*types=*/Case::kUpperCamel,
+           /*constants=*/Case::kUnknown,
+           /*methods=*/Case::kUpperCamel,
+           /*functions=*/Case::kUpperCamel,
+           /*fields=*/Case::kUpperCamel,
+           /*variables=*/Case::kLowerCamel,
+           /*variants=*/Case::kKeep,
+           /*enum_variant_seperator=*/"",
+           /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
+           /*namespaces=*/Case::kKeep,
+           /*namespace_seperator=*/"__",
+           /*object_prefix=*/"",
+           /*object_suffix=*/"",
+           /*keyword_prefix=*/"",
+           /*keyword_suffix=*/"_",
+           /*filenames=*/Case::kKeep,
+           /*directories=*/Case::kKeep,
+           /*output_path=*/"",
+           /*filename_suffix=*/"",
+           /*filename_extension=*/".lua" };
+}
+
 class LuaBfbsGenerator : public BaseBfbsGenerator {
  public:
   explicit LuaBfbsGenerator(const std::string &flatc_version)
@@ -44,14 +76,8 @@
         requires_(),
         current_obj_(nullptr),
         current_enum_(nullptr),
-        flatc_version_(flatc_version) {
-    static const char *const keywords[] = {
-      "and",      "break",  "do",   "else", "elseif", "end",  "false", "for",
-      "function", "goto",   "if",   "in",   "local",  "nil",  "not",   "or",
-      "repeat",   "return", "then", "true", "until",  "while"
-    };
-    keywords_.insert(std::begin(keywords), std::end(keywords));
-  }
+        flatc_version_(flatc_version),
+        namer_(LuaDefaultConfig(), LuaKeywords()) {}
 
   GeneratorStatus GenerateFromSchema(const r::Schema *schema)
       FLATBUFFERS_OVERRIDE {
@@ -76,14 +102,14 @@
 
       std::string ns;
       const std::string enum_name =
-          NormalizeName(Denamespace(enum_def->name(), ns));
+          namer_.Type(namer_.Denamespace(enum_def, ns));
 
       GenerateDocumentation(enum_def->documentation(), "", code);
       code += "local " + enum_name + " = {\n";
 
       ForAllEnumValues(enum_def, [&](const reflection::EnumVal *enum_val) {
         GenerateDocumentation(enum_val->documentation(), "  ", code);
-        code += "  " + NormalizeName(enum_val->name()) + " = " +
+        code += "  " + namer_.Variant(enum_val->name()->str()) + " = " +
                 NumToString(enum_val->value()) + ",\n";
       });
       code += "}\n";
@@ -107,7 +133,7 @@
 
       std::string ns;
       const std::string object_name =
-          NormalizeName(Denamespace(object->name(), ns));
+          namer_.Type(namer_.Denamespace(object, ns));
 
       GenerateDocumentation(object->documentation(), "", code);
 
@@ -149,14 +175,12 @@
         // Skip writing deprecated fields altogether.
         if (field->deprecated()) { return; }
 
-        const std::string field_name = NormalizeName(field->name());
-        const std::string field_name_camel_case =
-            ConvertCase(field_name, Case::kUpperCamel);
+        const std::string field_name = namer_.Field(field->name()->str());
         const r::BaseType base_type = field->type()->base_type();
 
         // Generate some fixed strings so we don't repeat outselves later.
         const std::string getter_signature =
-            "function mt:" + field_name_camel_case + "()\n";
+            "function mt:" + field_name + "()\n";
         const std::string offset_prefix = "local o = self.view:Offset(" +
                                           NumToString(field->offset()) + ")\n";
         const std::string offset_prefix_2 = "if o ~= 0 then\n";
@@ -201,7 +225,7 @@
             }
             case r::Obj: {
               if (object->is_struct()) {
-                code += "function mt:" + field_name_camel_case + "(obj)\n";
+                code += "function mt:" + field_name + "(obj)\n";
                 code += "  obj:Init(self.view.bytes, self.view.pos + " +
                         NumToString(field->offset()) + ")\n";
                 code += "  return obj\n";
@@ -252,7 +276,7 @@
             case r::Vector: {
               const r::BaseType vector_base_type = field->type()->element();
               int32_t element_size = field->type()->element_size();
-              code += "function mt:" + field_name_camel_case + "(j)\n";
+              code += "function mt:" + field_name + "(j)\n";
               code += "  " + offset_prefix;
               code += "  " + offset_prefix_2;
 
@@ -297,8 +321,7 @@
               // generate a helper function to get it as a byte string in
               // Lua.
               if (IsSingleByte(vector_base_type)) {
-                code += "function mt:" + field_name_camel_case +
-                        "AsString(start, stop)\n";
+                code += "function mt:" + field_name + "AsString(start, stop)\n";
                 code += "  return self.view:VectorAsString(" +
                         NumToString(field->offset()) + ", start, stop)\n";
                 code += "end\n";
@@ -307,7 +330,7 @@
 
               // We also make a new accessor to query just the length of the
               // vector.
-              code += "function mt:" + field_name_camel_case + "Length()\n";
+              code += "function mt:" + field_name + "Length()\n";
               code += "  " + offset_prefix;
               code += "  " + offset_prefix_2;
               code += "    return self.view:VectorLen(o)\n";
@@ -344,21 +367,20 @@
         ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
           if (field->deprecated()) { return; }
 
-          const std::string field_name = NormalizeName(field->name());
+          const std::string field_name = namer_.Field(field->name()->str());
+          const std::string variable_name =
+              namer_.Variable(field->name()->str());
 
-          code += "function " + object_name + ".Add" +
-                  ConvertCase(field_name, Case::kUpperCamel) + "(builder, " +
-                  ConvertCase(field_name, Case::kLowerCamel) + ")\n";
+          code += "function " + object_name + ".Add" + field_name +
+                  "(builder, " + variable_name + ")\n";
           code += "  builder:Prepend" + GenerateMethod(field) + "Slot(" +
-                  NumToString(field->id()) + ", " +
-                  ConvertCase(field_name, Case::kLowerCamel) + ", " +
+                  NumToString(field->id()) + ", " + variable_name + ", " +
                   DefaultValue(field) + ")\n";
           code += "end\n";
           code += "\n";
 
           if (IsVector(field->type()->base_type())) {
-            code += "function " + object_name + ".Start" +
-                    ConvertCase(field_name, Case::kUpperCamel) +
+            code += "function " + object_name + ".Start" + field_name +
                     "Vector(builder, numElems)\n";
 
             const int32_t element_size = field->type()->element_size();
@@ -406,11 +428,9 @@
       if (IsStructOrTable(field->type()->base_type())) {
         const r::Object *field_object = GetObject(field->type());
         signature += GenerateStructBuilderArgs(
-            field_object, prefix + NormalizeName(field->name()) + "_");
+            field_object, prefix + namer_.Variable(field->name()->str()) + "_");
       } else {
-        signature +=
-            ", " + prefix +
-            ConvertCase(NormalizeName(field->name()), Case::kLowerCamel);
+        signature += ", " + prefix + namer_.Variable(field->name()->str());
       }
     });
     return signature;
@@ -432,11 +452,10 @@
       if (IsStructOrTable(field->type()->base_type())) {
         const r::Object *field_object = GetObject(field->type());
         code += AppendStructBuilderBody(
-            field_object, prefix + NormalizeName(field->name()) + "_");
+            field_object, prefix + namer_.Variable(field->name()->str()) + "_");
       } else {
         code += "  builder:Prepend" + GenerateMethod(field) + "(" + prefix +
-                ConvertCase(NormalizeName(field->name()), Case::kLowerCamel) +
-                ")\n";
+                namer_.Variable(field->name()->str()) + ")\n";
       }
     });
 
@@ -445,9 +464,7 @@
 
   std::string GenerateMethod(const r::Field *field) const {
     const r::BaseType base_type = field->type()->base_type();
-    if (IsScalar(base_type)) {
-      return ConvertCase(GenerateType(base_type), Case::kUpperCamel);
-    }
+    if (IsScalar(base_type)) { return namer_.Type(GenerateType(base_type)); }
     if (IsStructOrTable(base_type)) { return "Struct"; }
     return "UOffsetTRelative";
   }
@@ -460,9 +477,7 @@
       case r::Vector: return GenerateGetter(type, true);
       default:
         return "self.view:Get(flatbuffers.N." +
-               ConvertCase(GenerateType(type, element_type),
-                           Case::kUpperCamel) +
-               ", ";
+               namer_.Type(GenerateType(type, element_type)) + ", ";
     }
   }
 
@@ -474,10 +489,8 @@
     switch (base_type) {
       case r::String: return "string";
       case r::Vector: return GenerateGetter(type, true);
-      case r::Obj: {
-        const r::Object *obj = GetObject(type);
-        return NormalizeName(Denamespace(obj->name()));
-      };
+      case r::Obj: return namer_.Type(namer_.Denamespace(GetObject(type)));
+
       default: return "*flatbuffers.Table";
     }
   }
@@ -515,14 +528,6 @@
     return "0";
   }
 
-  std::string NormalizeName(const std::string name) const {
-    return keywords_.find(name) == keywords_.end() ? name : "_" + name;
-  }
-
-  std::string NormalizeName(const flatbuffers::String *name) const {
-    return NormalizeName(name->str());
-  }
-
   void StartCodeBlock(const reflection::Enum *enum_def) {
     current_enum_ = enum_def;
     current_obj_ = nullptr;
@@ -544,11 +549,11 @@
 
     if (IsStructOrTable(type)) {
       const r::Object *object = GetObjectByIndex(field->type()->index());
-      if (object == current_obj_) { return Denamespace(object->name()); }
+      if (object == current_obj_) { return namer_.Denamespace(object); }
       type_name = object->name()->str();
     } else {
       const r::Enum *enum_def = GetEnumByIndex(field->type()->index());
-      if (enum_def == current_enum_) { return Denamespace(enum_def->name()); }
+      if (enum_def == current_enum_) { return namer_.Denamespace(enum_def); }
       type_name = enum_def->name()->str();
     }
 
@@ -600,7 +605,7 @@
     // Namespaces are '.' deliminted, so replace it with the path separator.
     std::string path = ns;
 
-    if (path.empty()) {
+    if (ns.empty()) {
       path = ".";
     } else {
       std::replace(path.begin(), path.end(), '.', '/');
@@ -608,7 +613,7 @@
 
     // TODO(derekbailey): figure out a save file without depending on util.h
     EnsureDirExists(path);
-    const std::string file_name = path + "/" + name + ".lua";
+    const std::string file_name = path + "/" + namer_.File(name);
     SaveFile(file_name.c_str(), code, false);
   }
 
@@ -617,6 +622,7 @@
   const r::Object *current_obj_;
   const r::Enum *current_enum_;
   const std::string flatc_version_;
+  const BfbsNamer namer_;
 };
 }  // namespace
 
diff --git a/src/bfbs_namer.h b/src/bfbs_namer.h
new file mode 100644
index 0000000..2c6e724
--- /dev/null
+++ b/src/bfbs_namer.h
@@ -0,0 +1,43 @@
+#ifndef FLATBUFFERS_BFBS_NAMER
+#define FLATBUFFERS_BFBS_NAMER
+
+#include "flatbuffers/reflection.h"
+#include "namer.h"
+
+namespace flatbuffers {
+
+// Provides Namer capabilities to types defined in the flatbuffers reflection.
+class BfbsNamer : public Namer {
+ public:
+  explicit BfbsNamer(Config config, std::set<std::string> keywords)
+      : Namer(config, std::move(keywords)) {}
+
+  using Namer::Constant;
+  using Namer::Denamespace;
+  using Namer::Directories;
+  using Namer::Field;
+  using Namer::File;
+  using Namer::Function;
+  using Namer::Method;
+  using Namer::Namespace;
+  using Namer::NamespacedType;
+  using Namer::ObjectType;
+  using Namer::Type;
+  using Namer::Variable;
+  using Namer::Variant;
+
+  template<typename T>
+  std::string Denamespace(T t, std::string &namespace_prefix,
+                          const char delimiter = '.') const {
+    return Namer::Denamespace(t->name()->c_str(), namespace_prefix, delimiter);
+  }
+
+  template<typename T>
+  std::string Denamespace(T t, const char delimiter = '.') const {
+    return Namer::Denamespace(t->name()->c_str(), delimiter);
+  }
+};
+
+}  // namespace flatbuffers
+
+#endif  // FLATBUFFERS_BFBS_NAMER
\ No newline at end of file
diff --git a/src/binary_annotator.cpp b/src/binary_annotator.cpp
new file mode 100644
index 0000000..dd0b454
--- /dev/null
+++ b/src/binary_annotator.cpp
@@ -0,0 +1,1419 @@
+#include "binary_annotator.h"
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "flatbuffers/reflection.h"
+#include "flatbuffers/verifier.h"
+
+namespace flatbuffers {
+namespace {
+
+static bool BinaryRegionSort(const BinaryRegion &a, const BinaryRegion &b) {
+  return a.offset < b.offset;
+}
+
+static void SetError(BinaryRegionComment &comment, BinaryRegionStatus status,
+                     std::string message = "") {
+  comment.status = status;
+  comment.status_message = message;
+}
+
+static BinaryRegion MakeBinaryRegion(
+    const uint64_t offset = 0, const uint64_t length = 0,
+    const BinaryRegionType type = BinaryRegionType::Unknown,
+    const uint64_t array_length = 0, const uint64_t points_to_offset = 0,
+    const BinaryRegionComment comment = {}) {
+  BinaryRegion region;
+  region.offset = offset;
+  region.length = length;
+  region.type = type;
+  region.array_length = array_length;
+  region.points_to_offset = points_to_offset;
+  region.comment = std::move(comment);
+  return region;
+}
+
+static BinarySection MakeBinarySection(
+    const std::string &name, const BinarySectionType type,
+    const std::vector<BinaryRegion> regions) {
+  BinarySection section;
+  section.name = name;
+  section.type = type;
+  section.regions = std::move(regions);
+  return section;
+}
+
+static BinarySection MakeSingleRegionBinarySection(const std::string &name,
+                                                   const BinarySectionType type,
+                                                   const BinaryRegion &region) {
+  std::vector<BinaryRegion> regions;
+  regions.push_back(region);
+  return MakeBinarySection(name, type, std::move(regions));
+}
+
+static bool IsNonZeroRegion(const uint64_t offset, const uint64_t length,
+                            const uint8_t *const binary) {
+  for (uint64_t i = offset; i < offset + length; ++i) {
+    if (binary[i] != 0) { return true; }
+  }
+  return false;
+}
+
+static bool IsPrintableRegion(const uint64_t offset, const uint64_t length,
+                              const uint8_t *const binary) {
+  for (uint64_t i = offset; i < offset + length; ++i) {
+    if (!isprint(binary[i])) { return false; }
+  }
+  return true;
+}
+
+static BinarySection GenerateMissingSection(const uint64_t offset,
+                                            const uint64_t length,
+                                            const uint8_t *const binary) {
+  std::vector<BinaryRegion> regions;
+
+  // Check if the region is all zeros or not, as that can tell us if it is
+  // padding or not.
+  if (IsNonZeroRegion(offset, length, binary)) {
+    // Some of the padding bytes are non-zero, so this might be an unknown
+    // section of the binary.
+    // TODO(dbaileychess): We could be a bit smarter with different sized
+    // alignments. For now, the 8 byte check encompasses all the smaller
+    // alignments.
+    BinaryRegionComment comment;
+    comment.type = BinaryRegionCommentType::Unknown;
+    if (length >= 8) {
+      SetError(comment, BinaryRegionStatus::WARN_NO_REFERENCES);
+    } else {
+      SetError(comment, BinaryRegionStatus::WARN_CORRUPTED_PADDING);
+    }
+
+    regions.push_back(MakeBinaryRegion(offset, length * sizeof(uint8_t),
+                                       BinaryRegionType::Unknown, length, 0,
+                                       comment));
+
+    return MakeBinarySection("no known references", BinarySectionType::Unknown,
+                             std::move(regions));
+  }
+
+  BinaryRegionComment comment;
+  comment.type = BinaryRegionCommentType::Padding;
+  if (length >= 8) {
+    SetError(comment, BinaryRegionStatus::WARN_PADDING_LENGTH);
+  }
+
+  // This region is most likely padding.
+  regions.push_back(MakeBinaryRegion(offset, length * sizeof(uint8_t),
+                                     BinaryRegionType::Uint8, length, 0,
+                                     comment));
+
+  return MakeBinarySection("", BinarySectionType::Padding, std::move(regions));
+}
+
+}  // namespace
+
+std::map<uint64_t, BinarySection> BinaryAnnotator::Annotate() {
+  flatbuffers::Verifier verifier(bfbs_, static_cast<size_t>(bfbs_length_));
+  if (!reflection::VerifySchemaBuffer(verifier)) { return {}; }
+
+  // The binary is too short to read as a flatbuffers.
+  // TODO(dbaileychess): We could spit out the annotated buffer sections, but
+  // I'm not sure if it is worth it.
+  if (binary_length_ < 4) { return {}; }
+
+  // Make sure we start with a clean slate.
+  vtables_.clear();
+  sections_.clear();
+
+  // First parse the header region which always start at offset 0.
+  // The returned offset will point to the root_table location.
+  const uint64_t root_table_offset = BuildHeader(0);
+
+  if (IsValidOffset(root_table_offset)) {
+    // Build the root table, and all else will be referenced from it.
+    BuildTable(root_table_offset, BinarySectionType::RootTable,
+               schema_->root_table());
+  }
+
+  // Now that all the sections are built, make sure the binary sections are
+  // contiguous.
+  FixMissingRegions();
+
+  // Then scan the area between BinarySections insert padding sections that are
+  // implied.
+  FixMissingSections();
+
+  return sections_;
+}
+
+uint64_t BinaryAnnotator::BuildHeader(const uint64_t header_offset) {
+  const auto root_table_offset = ReadScalar<uint32_t>(header_offset);
+
+  if (!root_table_offset.has_value()) {
+    // This shouldn't occur, since we validate the min size of the buffer
+    // before. But for completion sake, we shouldn't read passed the binary end.
+    return std::numeric_limits<uint64_t>::max();
+  }
+
+  std::vector<BinaryRegion> regions;
+  uint64_t offset = header_offset;
+  // TODO(dbaileychess): sized prefixed value
+
+  BinaryRegionComment root_offset_comment;
+  root_offset_comment.type = BinaryRegionCommentType::RootTableOffset;
+  root_offset_comment.name = schema_->root_table()->name()->str();
+
+  if (!IsValidOffset(root_table_offset.value())) {
+    SetError(root_offset_comment,
+             BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
+  }
+
+  regions.push_back(
+      MakeBinaryRegion(offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+                       root_table_offset.value(), root_offset_comment));
+  offset += sizeof(uint32_t);
+
+  if (IsValidRead(offset, flatbuffers::kFileIdentifierLength) &&
+      IsPrintableRegion(offset, flatbuffers::kFileIdentifierLength, binary_)) {
+    BinaryRegionComment comment;
+    comment.type = BinaryRegionCommentType::FileIdentifier;
+    // Check if the file identifier region has non-zero data, and assume its
+    // the file identifier. Otherwise, it will get filled in with padding
+    // later.
+    regions.push_back(MakeBinaryRegion(
+        offset, flatbuffers::kFileIdentifierLength * sizeof(uint8_t),
+        BinaryRegionType::Char, flatbuffers::kFileIdentifierLength, 0,
+        comment));
+  }
+
+  AddSection(header_offset, MakeBinarySection("", BinarySectionType::Header,
+                                              std::move(regions)));
+
+  return root_table_offset.value();
+}
+
+void BinaryAnnotator::BuildVTable(const uint64_t vtable_offset,
+                                  const reflection::Object *const table,
+                                  const uint64_t offset_of_referring_table) {
+  // First see if we have used this vtable before, if so skip building it again.
+  auto it = vtables_.find(vtable_offset);
+  if (it != vtables_.end()) { return; }
+
+  if (ContainsSection(vtable_offset)) { return; }
+
+  BinaryRegionComment vtable_size_comment;
+  vtable_size_comment.type = BinaryRegionCommentType::VTableSize;
+
+  const auto vtable_length = ReadScalar<uint16_t>(vtable_offset);
+  if (!vtable_length.has_value()) {
+    const uint64_t remaining = RemainingBytes(vtable_offset);
+
+    SetError(vtable_size_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+             "2");
+
+    AddSection(vtable_offset,
+               MakeSingleRegionBinarySection(
+                   table->name()->str(), BinarySectionType::VTable,
+                   MakeBinaryRegion(vtable_offset, remaining,
+                                    BinaryRegionType::Unknown, remaining, 0,
+                                    vtable_size_comment)));
+    return;
+  }
+
+  // Vtables start with the size of the vtable
+  const uint16_t vtable_size = vtable_length.value();
+
+  if (!IsValidOffset(vtable_offset + vtable_size - 1)) {
+    SetError(vtable_size_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
+    // The vtable_size points to off the end of the binary.
+    AddSection(vtable_offset,
+               MakeSingleRegionBinarySection(
+                   table->name()->str(), BinarySectionType::VTable,
+                   MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
+                                    BinaryRegionType::Uint16, 0, 0,
+                                    vtable_size_comment)));
+
+    return;
+  } else if (vtable_size < 2 * sizeof(uint16_t)) {
+    SetError(vtable_size_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_SHORT,
+             "4");
+    // The size includes itself and the table size which are both uint16_t.
+    AddSection(vtable_offset,
+               MakeSingleRegionBinarySection(
+                   table->name()->str(), BinarySectionType::VTable,
+                   MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
+                                    BinaryRegionType::Uint16, 0, 0,
+                                    vtable_size_comment)));
+    return;
+  }
+
+  std::vector<BinaryRegion> regions;
+
+  regions.push_back(MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
+                                     BinaryRegionType::Uint16, 0, 0,
+                                     vtable_size_comment));
+  uint64_t offset = vtable_offset + sizeof(uint16_t);
+
+  BinaryRegionComment ref_table_len_comment;
+  ref_table_len_comment.type =
+      BinaryRegionCommentType::VTableRefferingTableLength;
+
+  // Ensure we can read the next uint16_t field, which is the size of the
+  // referring table.
+  const auto table_length = ReadScalar<uint16_t>(offset);
+
+  if (!table_length.has_value()) {
+    const uint64_t remaining = RemainingBytes(offset);
+    SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+             "2");
+
+    AddSection(offset, MakeSingleRegionBinarySection(
+                           table->name()->str(), BinarySectionType::VTable,
+                           MakeBinaryRegion(
+                               offset, remaining, BinaryRegionType::Unknown,
+                               remaining, 0, ref_table_len_comment)));
+    return;
+  }
+
+  // Then they have the size of the table they reference.
+  const uint16_t table_size = table_length.value();
+
+  if (!IsValidOffset(offset_of_referring_table + table_size - 1)) {
+    SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
+  } else if (table_size < 4) {
+    SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_SHORT,
+             "4");
+  }
+
+  regions.push_back(MakeBinaryRegion(offset, sizeof(uint16_t),
+                                     BinaryRegionType::Uint16, 0, 0,
+                                     ref_table_len_comment));
+  offset += sizeof(uint16_t);
+
+  const uint64_t offset_start = offset;
+
+  // A mapping between field (and its id) to the relative offset (uin16_t) from
+  // the start of the table.
+  std::map<uint16_t, VTable::Entry> fields;
+
+  // Counter for determining if the binary has more vtable entries than the
+  // schema provided. This can occur if the binary was created at a newer schema
+  // version and is being processed with an older one.
+  uint16_t fields_processed = 0;
+
+  // Loop over all the fields.
+  ForAllFields(table, /*reverse=*/false, [&](const reflection::Field *field) {
+    const uint64_t field_offset = offset_start + field->id() * sizeof(uint16_t);
+
+    if (field_offset >= vtable_offset + vtable_size) {
+      // This field_offset is too large for this vtable, so it must come from a
+      // newer schema than the binary was create with or the binary writer did
+      // not write it. For either case, it is safe to ignore.
+
+      // TODO(dbaileychess): We could show which fields are not set an their
+      // default values if we want. We just need a way to make it obvious that
+      // it isn't part of the buffer.
+      return;
+    }
+
+    BinaryRegionComment field_comment;
+    field_comment.type = BinaryRegionCommentType::VTableFieldOffset;
+    field_comment.name = std::string(field->name()->c_str()) +
+                         "` (id: " + std::to_string(field->id()) + ")";
+
+    const auto offset_from_table = ReadScalar<uint16_t>(field_offset);
+
+    if (!offset_from_table.has_value()) {
+      const uint64_t remaining = RemainingBytes(field_offset);
+
+      SetError(field_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
+      regions.push_back(MakeBinaryRegion(field_offset, remaining,
+                                         BinaryRegionType::Unknown, remaining,
+                                         0, field_comment));
+
+      return;
+    }
+
+    if (!IsValidOffset(offset_of_referring_table + offset_from_table.value() -
+                       1)) {
+      SetError(field_comment, BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
+      regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
+                                         BinaryRegionType::VOffset, 0, 0,
+                                         field_comment));
+      return;
+    }
+
+    VTable::Entry entry;
+    entry.field = field;
+    entry.offset_from_table = offset_from_table.value();
+    fields.insert(std::make_pair(field->id(), entry));
+
+    std::string default_label;
+    if (offset_from_table.value() == 0) {
+      // Not present, so could be default or be optional.
+      if (field->required()) {
+        SetError(field_comment,
+                 BinaryRegionStatus::ERROR_REQUIRED_FIELD_NOT_PRESENT);
+        // If this is a required field, make it known this is an error.
+        regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
+                                           BinaryRegionType::VOffset, 0, 0,
+                                           field_comment));
+        return;
+      } else {
+        // Its an optional field, so get the default value and interpret and
+        // provided an annotation for it.
+        if (IsScalar(field->type()->base_type())) {
+          default_label += "<defaults to ";
+          default_label += IsFloat(field->type()->base_type())
+                               ? std::to_string(field->default_real())
+                               : std::to_string(field->default_integer());
+          default_label += "> (";
+        } else {
+          default_label += "<null> (";
+        }
+        default_label +=
+            reflection::EnumNameBaseType(field->type()->base_type());
+        default_label += ")";
+      }
+    }
+    field_comment.default_value = default_label;
+
+    regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
+                                       BinaryRegionType::VOffset, 0, 0,
+                                       field_comment));
+
+    fields_processed++;
+  });
+
+  // Check if we covered all the expectant fields. If not, we need to add them
+  // as unknown fields.
+  uint16_t expectant_vtable_fields =
+      (vtable_size - sizeof(uint16_t) - sizeof(uint16_t)) / sizeof(uint16_t);
+
+  // Prevent a bad binary from declaring a really large vtable_size, that we can
+  // not indpendently verify.
+  expectant_vtable_fields = std::min(
+      static_cast<uint16_t>(fields_processed * 3), expectant_vtable_fields);
+
+  for (uint16_t id = fields_processed; id < expectant_vtable_fields; ++id) {
+    const uint64_t field_offset = offset_start + id * sizeof(uint16_t);
+
+    const auto offset_from_table = ReadScalar<uint16_t>(field_offset);
+
+    BinaryRegionComment field_comment;
+    field_comment.type = BinaryRegionCommentType::VTableUnknownFieldOffset;
+    field_comment.index = id;
+
+    if (!offset_from_table.has_value()) {
+      const uint64_t remaining = RemainingBytes(field_offset);
+      SetError(field_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
+      regions.push_back(MakeBinaryRegion(field_offset, remaining,
+                                         BinaryRegionType::Unknown, remaining,
+                                         0, field_comment));
+      continue;
+    }
+
+    VTable::Entry entry;
+    entry.field = nullptr;  // No field to reference.
+    entry.offset_from_table = offset_from_table.value();
+    fields.insert(std::make_pair(id, entry));
+
+    regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
+                                       BinaryRegionType::VOffset, 0, 0,
+                                       field_comment));
+  }
+
+  sections_[vtable_offset] = MakeBinarySection(
+      table->name()->str(), BinarySectionType::VTable, std::move(regions));
+
+  VTable vtable;
+  vtable.fields = std::move(fields);
+  vtable.table_size = table_size;
+  vtable.vtable_size = vtable_size;
+
+  vtables_[vtable_offset] = vtable;
+}
+
+void BinaryAnnotator::BuildTable(const uint64_t table_offset,
+                                 const BinarySectionType type,
+                                 const reflection::Object *const table) {
+  if (ContainsSection(table_offset)) { return; }
+
+  BinaryRegionComment vtable_offset_comment;
+  vtable_offset_comment.type = BinaryRegionCommentType::TableVTableOffset;
+
+  const auto vtable_soffset = ReadScalar<int32_t>(table_offset);
+
+  if (!vtable_soffset.has_value()) {
+    const uint64_t remaining = RemainingBytes(table_offset);
+    SetError(vtable_offset_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+             "4");
+
+    AddSection(
+        table_offset,
+        MakeSingleRegionBinarySection(
+            table->name()->str(), type,
+            MakeBinaryRegion(table_offset, remaining, BinaryRegionType::Unknown,
+                             remaining, 0, vtable_offset_comment)));
+
+    // If there aren't enough bytes left to read the vtable offset, there is
+    // nothing we can do.
+    return;
+  }
+
+  // Tables start with the vtable
+  const uint64_t vtable_offset = table_offset - vtable_soffset.value();
+
+  if (!IsValidOffset(vtable_offset)) {
+    SetError(vtable_offset_comment,
+             BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
+
+    AddSection(table_offset,
+               MakeSingleRegionBinarySection(
+                   table->name()->str(), type,
+                   MakeBinaryRegion(table_offset, sizeof(int32_t),
+                                    BinaryRegionType::SOffset, 0, vtable_offset,
+                                    vtable_offset_comment)));
+
+    // There isn't much to do with an invalid vtable offset, as we won't be able
+    // to intepret the rest of the table fields.
+    return;
+  }
+
+  std::vector<BinaryRegion> regions;
+  regions.push_back(MakeBinaryRegion(table_offset, sizeof(int32_t),
+                                     BinaryRegionType::SOffset, 0,
+                                     vtable_offset, vtable_offset_comment));
+
+  // Parse the vtable first so we know what the rest of the fields in the table
+  // are.
+  BuildVTable(vtable_offset, table, table_offset);
+
+  auto vtable_entry = vtables_.find(vtable_offset);
+  if (vtable_entry == vtables_.end()) {
+    // There is no valid vtable for this table, so we cannot process the rest of
+    // the table entries.
+    return;
+  }
+
+  const VTable &vtable = vtable_entry->second;
+
+  // This is the size and length of this table.
+  const uint16_t table_size = vtable.table_size;
+  uint64_t table_end_offset = table_offset + table_size;
+
+  if (!IsValidOffset(table_end_offset - 1)) {
+    // We already validated the table size in BuildVTable, but we have to make
+    // sure we don't use a bad value here.
+    table_end_offset = binary_length_;
+  }
+
+  // We need to iterate over the vtable fields by their offset in the binary,
+  // not by their IDs. So copy them over to another vector that we can sort on
+  // the offset_from_table property.
+  std::vector<VTable::Entry> fields;
+  for (const auto &vtable_field : vtable.fields) {
+    fields.push_back(vtable_field.second);
+  }
+
+  std::stable_sort(fields.begin(), fields.end(),
+                   [](const VTable::Entry &a, const VTable::Entry &b) {
+                     return a.offset_from_table < b.offset_from_table;
+                   });
+
+  // Iterate over all the fields by order of their offset.
+  for (size_t i = 0; i < fields.size(); ++i) {
+    const reflection::Field *field = fields[i].field;
+    const uint16_t offset_from_table = fields[i].offset_from_table;
+
+    if (offset_from_table == 0) {
+      // Skip non-present fields.
+      continue;
+    }
+
+    // The field offsets are relative to the start of the table.
+    const uint64_t field_offset = table_offset + offset_from_table;
+
+    if (!IsValidOffset(field_offset)) {
+      // The field offset is larger than the binary, nothing we can do.
+      continue;
+    }
+
+    // We have a vtable entry for a non-existant field, that means its a binary
+    // generated by a newer schema than we are currently processing.
+    if (field == nullptr) {
+      // Calculate the length of this unknown field.
+      const uint64_t unknown_field_length =
+          // Check if there is another unknown field after this one.
+          ((i + 1 < fields.size())
+               ? table_offset + fields[i + 1].offset_from_table
+               // Otherwise use the known end of the table.
+               : table_end_offset) -
+          field_offset;
+
+      if (unknown_field_length == 0) { continue; }
+
+      std::string hint;
+
+      if (unknown_field_length == 4) {
+        const auto relative_offset = ReadScalar<uint32_t>(field_offset);
+        if (relative_offset.has_value()) {
+          // The field is 4 in length, so it could be an offset? Provide a hint.
+          hint += "<possibly an offset? Check Loc: +0x";
+          hint += ToHex(field_offset + relative_offset.value());
+          hint += ">";
+        }
+      }
+
+      BinaryRegionComment unknown_field_comment;
+      unknown_field_comment.type = BinaryRegionCommentType::TableUnknownField;
+
+      if (!IsValidRead(field_offset, unknown_field_length)) {
+        const uint64_t remaining = RemainingBytes(field_offset);
+
+        SetError(unknown_field_comment,
+                 BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+                 std::to_string(unknown_field_length));
+
+        regions.push_back(MakeBinaryRegion(field_offset, remaining,
+                                           BinaryRegionType::Unknown, remaining,
+                                           0, unknown_field_comment));
+        continue;
+      }
+
+      unknown_field_comment.default_value = hint;
+
+      regions.push_back(MakeBinaryRegion(
+          field_offset, unknown_field_length, BinaryRegionType::Unknown,
+          unknown_field_length, 0, unknown_field_comment));
+      continue;
+    }
+
+    if (IsScalar(field->type()->base_type())) {
+      // These are the raw values store in the table.
+      const uint64_t type_size = GetTypeSize(field->type()->base_type());
+      const BinaryRegionType region_type =
+          GetRegionType(field->type()->base_type());
+
+      BinaryRegionComment scalar_field_comment;
+      scalar_field_comment.type = BinaryRegionCommentType::TableField;
+      scalar_field_comment.name =
+          std::string(field->name()->c_str()) + "` (" +
+          reflection::EnumNameBaseType(field->type()->base_type()) + ")";
+
+      if (!IsValidRead(field_offset, type_size)) {
+        const uint64_t remaining = RemainingBytes(field_offset);
+        SetError(scalar_field_comment,
+                 BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+                 std::to_string(type_size));
+
+        regions.push_back(MakeBinaryRegion(field_offset, remaining,
+                                           BinaryRegionType::Unknown, remaining,
+                                           0, scalar_field_comment));
+        continue;
+      }
+
+      if (IsUnionType(field)) {
+        // This is a type for a union. Validate the value
+        const auto enum_value = ReadScalar<uint8_t>(field_offset);
+
+        // This should always have a value, due to the IsValidRead check above.
+        if (!IsValidUnionValue(field, enum_value.value())) {
+          SetError(scalar_field_comment,
+                   BinaryRegionStatus::ERROR_INVALID_UNION_TYPE);
+
+          regions.push_back(MakeBinaryRegion(field_offset, type_size,
+                                             region_type, 0, 0,
+                                             scalar_field_comment));
+          continue;
+        }
+      }
+
+      regions.push_back(MakeBinaryRegion(field_offset, type_size, region_type,
+                                         0, 0, scalar_field_comment));
+      continue;
+    }
+
+    // Read the offset
+    const auto offset_from_field = ReadScalar<uint32_t>(field_offset);
+    uint64_t offset_of_next_item = 0;
+    BinaryRegionComment offset_field_comment;
+    offset_field_comment.type = BinaryRegionCommentType::TableOffsetField;
+    offset_field_comment.name = field->name()->c_str();
+    const std::string offset_prefix =
+        "offset to field `" + std::string(field->name()->c_str()) + "`";
+
+    // Validate any field that isn't inline (i.e., non-structs).
+    if (!IsInlineField(field)) {
+      if (!offset_from_field.has_value()) {
+        const uint64_t remaining = RemainingBytes(field_offset);
+
+        SetError(offset_field_comment,
+                 BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
+
+        regions.push_back(MakeBinaryRegion(field_offset, remaining,
+                                           BinaryRegionType::Unknown, remaining,
+                                           0, offset_field_comment));
+        continue;
+      }
+
+      offset_of_next_item = field_offset + offset_from_field.value();
+
+      if (!IsValidOffset(offset_of_next_item)) {
+        SetError(offset_field_comment,
+                 BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
+        regions.push_back(MakeBinaryRegion(
+            field_offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+            offset_of_next_item, offset_field_comment));
+        continue;
+      }
+    }
+
+    switch (field->type()->base_type()) {
+      case reflection::BaseType::Obj: {
+        const reflection::Object *next_object =
+            schema_->objects()->Get(field->type()->index());
+
+        if (next_object->is_struct()) {
+          // Structs are stored inline.
+          BuildStruct(field_offset, regions, next_object);
+        } else {
+          offset_field_comment.default_value = "(table)";
+
+          regions.push_back(MakeBinaryRegion(
+              field_offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+              offset_of_next_item, offset_field_comment));
+
+          BuildTable(offset_of_next_item, BinarySectionType::Table,
+                     next_object);
+        }
+      } break;
+
+      case reflection::BaseType::String: {
+        offset_field_comment.default_value = "(string)";
+        regions.push_back(MakeBinaryRegion(
+            field_offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+            offset_of_next_item, offset_field_comment));
+        BuildString(offset_of_next_item, table, field);
+      } break;
+
+      case reflection::BaseType::Vector: {
+        offset_field_comment.default_value = "(vector)";
+        regions.push_back(MakeBinaryRegion(
+            field_offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+            offset_of_next_item, offset_field_comment));
+        BuildVector(offset_of_next_item, table, field, table_offset, vtable);
+      } break;
+
+      case reflection::BaseType::Union: {
+        const uint64_t union_offset = offset_of_next_item;
+
+        // The union type field is always one less than the union itself.
+        const uint16_t union_type_id = field->id() - 1;
+
+        auto vtable_field = vtable.fields.find(union_type_id);
+        if (vtable_field == vtable.fields.end()) {
+          // TODO(dbaileychess): need to capture this error condition.
+          break;
+        }
+        offset_field_comment.default_value = "(union)";
+
+        const uint64_t type_offset =
+            table_offset + vtable_field->second.offset_from_table;
+
+        const auto realized_type = ReadScalar<uint8_t>(type_offset);
+        if (!realized_type.has_value()) {
+          const uint64_t remaining = RemainingBytes(type_offset);
+          SetError(offset_field_comment,
+                   BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "1");
+          regions.push_back(MakeBinaryRegion(
+              type_offset, remaining, BinaryRegionType::Unknown, remaining, 0,
+              offset_field_comment));
+          continue;
+        }
+
+        if (!IsValidUnionValue(field, realized_type.value())) {
+          // We already export an error in the union type field, so just skip
+          // building the union itself and it will default to an unreference
+          // Binary section.
+          continue;
+        }
+
+        const std::string enum_type =
+            BuildUnion(union_offset, realized_type.value(), field);
+
+        offset_field_comment.default_value =
+            "(union of type `" + enum_type + "`)";
+
+        regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint32_t),
+                                           BinaryRegionType::UOffset, 0,
+                                           union_offset, offset_field_comment));
+
+      } break;
+
+      default: break;
+    }
+  }
+
+  // Handle the case where there is padding after the last known binary
+  // region. Calculate where we left off towards the expected end of the
+  // table.
+  const uint64_t i = regions.back().offset + regions.back().length + 1;
+
+  if (i < table_end_offset) {
+    const uint64_t pad_bytes = table_end_offset - i + 1;
+
+    BinaryRegionComment padding_comment;
+    padding_comment.type = BinaryRegionCommentType::Padding;
+
+    regions.push_back(MakeBinaryRegion(i - 1, pad_bytes * sizeof(uint8_t),
+                                       BinaryRegionType::Uint8, pad_bytes, 0,
+                                       padding_comment));
+  }
+
+  AddSection(table_offset,
+             MakeBinarySection(table->name()->str(), type, std::move(regions)));
+}
+
+uint64_t BinaryAnnotator::BuildStruct(const uint64_t struct_offset,
+                                      std::vector<BinaryRegion> &regions,
+                                      const reflection::Object *const object) {
+  if (!object->is_struct()) { return struct_offset; }
+  uint64_t offset = struct_offset;
+
+  // Loop over all the fields in increasing order
+  ForAllFields(object, /*reverse=*/false, [&](const reflection::Field *field) {
+    if (IsScalar(field->type()->base_type())) {
+      // Structure Field value
+      const uint64_t type_size = GetTypeSize(field->type()->base_type());
+      const BinaryRegionType region_type =
+          GetRegionType(field->type()->base_type());
+
+      BinaryRegionComment comment;
+      comment.type = BinaryRegionCommentType::StructField;
+      comment.name =
+          std::string(object->name()->c_str()) + "." + field->name()->c_str();
+      comment.default_value = "(" +
+                              std::string(reflection::EnumNameBaseType(
+                                  field->type()->base_type())) +
+                              ")";
+
+      if (!IsValidRead(offset, type_size)) {
+        const uint64_t remaining = RemainingBytes(offset);
+        SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+                 std::to_string(type_size));
+        regions.push_back(MakeBinaryRegion(offset, remaining,
+                                           BinaryRegionType::Unknown, remaining,
+                                           0, comment));
+
+        // TODO(dbaileychess): Should I bail out here? This sets offset to the
+        // end of the binary. So all other reads in the loop should fail.
+        offset += remaining;
+        return;
+      }
+
+      regions.push_back(
+          MakeBinaryRegion(offset, type_size, region_type, 0, 0, comment));
+      offset += type_size;
+    } else if (field->type()->base_type() == reflection::BaseType::Obj) {
+      // Structs are stored inline, even when nested.
+      offset = BuildStruct(offset, regions,
+                           schema_->objects()->Get(field->type()->index()));
+    } else if (field->type()->base_type() == reflection::BaseType::Array) {
+      const bool is_scalar = IsScalar(field->type()->element());
+      const uint64_t type_size = GetTypeSize(field->type()->element());
+      const BinaryRegionType region_type =
+          GetRegionType(field->type()->element());
+
+      // Arrays are just repeated structures.
+      for (uint16_t i = 0; i < field->type()->fixed_length(); ++i) {
+        if (is_scalar) {
+          BinaryRegionComment array_comment;
+          array_comment.type = BinaryRegionCommentType::ArrayField;
+          array_comment.name = std::string(object->name()->c_str()) + "." +
+                               field->name()->c_str();
+          array_comment.index = i;
+          array_comment.default_value =
+              "(" +
+              std::string(
+                  reflection::EnumNameBaseType(field->type()->element())) +
+              ")";
+
+          if (!IsValidRead(offset, type_size)) {
+            const uint64_t remaining = RemainingBytes(offset);
+
+            SetError(array_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+                     std::to_string(type_size));
+
+            regions.push_back(MakeBinaryRegion(offset, remaining,
+                                               BinaryRegionType::Unknown,
+                                               remaining, 0, array_comment));
+
+            // TODO(dbaileychess): Should I bail out here? This sets offset to
+            // the end of the binary. So all other reads in the loop should
+            // fail.
+            offset += remaining;
+            break;
+          }
+
+          regions.push_back(MakeBinaryRegion(offset, type_size, region_type, 0,
+                                             0, array_comment));
+
+          offset += type_size;
+        } else {
+          // Array of Structs.
+          //
+          // TODO(dbaileychess): This works, but the comments on the fields lose
+          // some context. Need to figure a way how to plumb the nested arrays
+          // comments together that isn't too confusing.
+          offset = BuildStruct(offset, regions,
+                               schema_->objects()->Get(field->type()->index()));
+        }
+      }
+    }
+
+    // Insert any padding after this field.
+    const uint16_t padding = field->padding();
+    if (padding > 0 && IsValidOffset(offset + padding)) {
+      BinaryRegionComment padding_comment;
+      padding_comment.type = BinaryRegionCommentType::Padding;
+
+      regions.push_back(MakeBinaryRegion(offset, padding,
+                                         BinaryRegionType::Uint8, padding, 0,
+                                         padding_comment));
+      offset += padding;
+    }
+  });
+
+  return offset;
+}
+
+void BinaryAnnotator::BuildString(const uint64_t string_offset,
+                                  const reflection::Object *const table,
+                                  const reflection::Field *const field) {
+  // Check if we have already generated this string section, and this is a
+  // shared string instance.
+  if (ContainsSection(string_offset)) { return; }
+
+  std::vector<BinaryRegion> regions;
+  const auto string_length = ReadScalar<uint32_t>(string_offset);
+
+  BinaryRegionComment string_length_comment;
+  string_length_comment.type = BinaryRegionCommentType::StringLength;
+
+  if (!string_length.has_value()) {
+    const uint64_t remaining = RemainingBytes(string_offset);
+
+    SetError(string_length_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+             "4");
+
+    regions.push_back(MakeBinaryRegion(string_offset, remaining,
+                                       BinaryRegionType::Unknown, remaining, 0,
+                                       string_length_comment));
+
+  } else {
+    const uint32_t string_size = string_length.value();
+    const uint64_t string_end =
+        string_offset + sizeof(uint32_t) + string_size + sizeof(char);
+
+    if (!IsValidOffset(string_end - 1)) {
+      SetError(string_length_comment,
+               BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
+
+      regions.push_back(MakeBinaryRegion(string_offset, sizeof(uint32_t),
+                                         BinaryRegionType::Uint32, 0, 0,
+                                         string_length_comment));
+    } else {
+      regions.push_back(MakeBinaryRegion(string_offset, sizeof(uint32_t),
+                                         BinaryRegionType::Uint32, 0, 0,
+                                         string_length_comment));
+
+      BinaryRegionComment string_comment;
+      string_comment.type = BinaryRegionCommentType::StringValue;
+
+      regions.push_back(MakeBinaryRegion(string_offset + sizeof(uint32_t),
+                                         string_size, BinaryRegionType::Char,
+                                         string_size, 0, string_comment));
+
+      BinaryRegionComment string_terminator_comment;
+      string_terminator_comment.type =
+          BinaryRegionCommentType::StringTerminator;
+
+      regions.push_back(MakeBinaryRegion(
+          string_offset + sizeof(uint32_t) + string_size, sizeof(char),
+          BinaryRegionType::Char, 0, 0, string_terminator_comment));
+    }
+  }
+
+  AddSection(string_offset,
+             MakeBinarySection(std::string(table->name()->c_str()) + "." +
+                                   field->name()->c_str(),
+                               BinarySectionType::String, std::move(regions)));
+}
+
+void BinaryAnnotator::BuildVector(const uint64_t vector_offset,
+                                  const reflection::Object *const table,
+                                  const reflection::Field *const field,
+                                  const uint64_t parent_table_offset,
+                                  const VTable &vtable) {
+  if (ContainsSection(vector_offset)) { return; }
+
+  BinaryRegionComment vector_length_comment;
+  vector_length_comment.type = BinaryRegionCommentType::VectorLength;
+
+  const auto vector_length = ReadScalar<uint32_t>(vector_offset);
+  if (!vector_length.has_value()) {
+    const uint64_t remaining = RemainingBytes(vector_offset);
+    SetError(vector_length_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+             "4");
+
+    AddSection(
+        vector_offset,
+        MakeSingleRegionBinarySection(
+            std::string(table->name()->c_str()) + "." + field->name()->c_str(),
+            BinarySectionType::Vector,
+            MakeBinaryRegion(vector_offset, remaining,
+                             BinaryRegionType::Unknown, remaining, 0,
+                             vector_length_comment)));
+    return;
+  }
+
+  // Validate there are enough bytes left in the binary to process all the
+  // items.
+  const uint64_t last_item_offset =
+      vector_offset + sizeof(uint32_t) +
+      vector_length.value() * GetElementSize(field);
+
+  if (!IsValidOffset(last_item_offset - 1)) {
+    SetError(vector_length_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
+    AddSection(
+        vector_offset,
+        MakeSingleRegionBinarySection(
+            std::string(table->name()->c_str()) + "." + field->name()->c_str(),
+            BinarySectionType::Vector,
+            MakeBinaryRegion(vector_offset, sizeof(uint32_t),
+                             BinaryRegionType::Uint32, 0, 0,
+                             vector_length_comment)));
+
+    return;
+  }
+
+  std::vector<BinaryRegion> regions;
+
+  regions.push_back(MakeBinaryRegion(vector_offset, sizeof(uint32_t),
+                                     BinaryRegionType::Uint32, 0, 0,
+                                     vector_length_comment));
+
+  uint64_t offset = vector_offset + sizeof(uint32_t);
+
+  switch (field->type()->element()) {
+    case reflection::BaseType::Obj: {
+      const reflection::Object *object =
+          schema_->objects()->Get(field->type()->index());
+
+      if (object->is_struct()) {
+        // Vector of structs
+        for (size_t i = 0; i < vector_length.value(); ++i) {
+          // Structs are inline to the vector.
+          const uint64_t next_offset = BuildStruct(offset, regions, object);
+          if (next_offset == offset) { break; }
+          offset = next_offset;
+        }
+      } else {
+        // Vector of objects
+        for (size_t i = 0; i < vector_length.value(); ++i) {
+          BinaryRegionComment vector_object_comment;
+          vector_object_comment.type =
+              BinaryRegionCommentType::VectorTableValue;
+          vector_object_comment.index = i;
+
+          const auto table_relative_offset = ReadScalar<uint32_t>(offset);
+          if (!table_relative_offset.has_value()) {
+            const uint64_t remaining = RemainingBytes(offset);
+            SetError(vector_object_comment,
+                     BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
+
+            regions.push_back(
+                MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
+                                 remaining, 0, vector_object_comment));
+            break;
+          }
+
+          // The table offset is relative from the offset location itself.
+          const uint64_t table_offset = offset + table_relative_offset.value();
+
+          if (!IsValidOffset(table_offset)) {
+            SetError(vector_object_comment,
+                     BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
+            regions.push_back(MakeBinaryRegion(
+                offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+                table_offset, vector_object_comment));
+
+            offset += sizeof(uint32_t);
+            continue;
+          }
+
+          if (table_offset == parent_table_offset) {
+            SetError(vector_object_comment,
+                     BinaryRegionStatus::ERROR_CYCLE_DETECTED);
+            // A cycle detected where a table vector field is pointing to
+            // itself. This should only happen in corrupted files.
+            regions.push_back(MakeBinaryRegion(
+                offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+                table_offset, vector_object_comment));
+
+            offset += sizeof(uint32_t);
+            continue;
+          }
+
+          regions.push_back(MakeBinaryRegion(
+              offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+              table_offset, vector_object_comment));
+
+          offset += sizeof(uint32_t);
+
+          BuildTable(table_offset, BinarySectionType::Table, object);
+        }
+      }
+    } break;
+    case reflection::BaseType::String: {
+      // Vector of strings
+      for (size_t i = 0; i < vector_length.value(); ++i) {
+        BinaryRegionComment vector_object_comment;
+        vector_object_comment.type = BinaryRegionCommentType::VectorStringValue;
+        vector_object_comment.index = i;
+
+        const auto string_relative_offset = ReadScalar<uint32_t>(offset);
+        if (!string_relative_offset.has_value()) {
+          const uint64_t remaining = RemainingBytes(offset);
+
+          SetError(vector_object_comment,
+                   BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
+
+          regions.push_back(
+              MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
+                               remaining, 0, vector_object_comment));
+          break;
+        }
+
+        // The string offset is relative from the offset location itself.
+        const uint64_t string_offset = offset + string_relative_offset.value();
+
+        if (!IsValidOffset(string_offset)) {
+          SetError(vector_object_comment,
+                   BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
+          regions.push_back(MakeBinaryRegion(
+              offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+              string_offset, vector_object_comment));
+
+          offset += sizeof(uint32_t);
+          continue;
+        }
+
+        regions.push_back(MakeBinaryRegion(
+            offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
+            string_offset, vector_object_comment));
+
+        BuildString(string_offset, table, field);
+
+        offset += sizeof(uint32_t);
+      }
+    } break;
+    case reflection::BaseType::Union: {
+      // Vector of unions
+      // Unions have both their realized type (uint8_t for now) that are
+      // stored separately. These are stored in the field->index() - 1
+      // location.
+      const uint16_t union_type_vector_id = field->id() - 1;
+
+      auto vtable_entry = vtable.fields.find(union_type_vector_id);
+      if (vtable_entry == vtable.fields.end()) {
+        // TODO(dbaileychess): need to capture this error condition.
+        break;
+      }
+
+      const uint64_t union_type_vector_field_offset =
+          parent_table_offset + vtable_entry->second.offset_from_table;
+
+      const auto union_type_vector_field_relative_offset =
+          ReadScalar<uint16_t>(union_type_vector_field_offset);
+
+      if (!union_type_vector_field_relative_offset.has_value()) {
+        const uint64_t remaining = RemainingBytes(offset);
+        BinaryRegionComment vector_union_comment;
+        vector_union_comment.type = BinaryRegionCommentType::VectorUnionValue;
+        SetError(vector_union_comment,
+                 BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
+
+        regions.push_back(MakeBinaryRegion(offset, remaining,
+                                           BinaryRegionType::Unknown, remaining,
+                                           0, vector_union_comment));
+
+        break;
+      }
+
+      // Get the offset to the first type (the + sizeof(uint32_t) is to skip
+      // over the vector length which we already know). Validation happens
+      // within the loop below.
+      const uint64_t union_type_vector_data_offset =
+          union_type_vector_field_offset +
+          union_type_vector_field_relative_offset.value() + sizeof(uint32_t);
+
+      for (size_t i = 0; i < vector_length.value(); ++i) {
+        BinaryRegionComment comment;
+        comment.type = BinaryRegionCommentType::VectorUnionValue;
+        comment.index = i;
+
+        const auto union_relative_offset = ReadScalar<uint32_t>(offset);
+        if (!union_relative_offset.has_value()) {
+          const uint64_t remaining = RemainingBytes(offset);
+
+          SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
+
+          regions.push_back(MakeBinaryRegion(offset, remaining,
+                                             BinaryRegionType::Unknown,
+                                             remaining, 0, comment));
+
+          break;
+        }
+
+        // The union offset is relative from the offset location itself.
+        const uint64_t union_offset = offset + union_relative_offset.value();
+
+        if (!IsValidOffset(union_offset)) {
+          SetError(comment, BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
+
+          regions.push_back(MakeBinaryRegion(offset, sizeof(uint32_t),
+                                             BinaryRegionType::UOffset, 0,
+                                             union_offset, comment));
+          continue;
+        }
+
+        const auto realized_type =
+            ReadScalar<uint8_t>(union_type_vector_data_offset + i);
+
+        if (!realized_type.has_value()) {
+          SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "1");
+          regions.push_back(MakeBinaryRegion(
+              offset, 0, BinaryRegionType::Unknown, 0, 0, comment));
+          continue;
+        }
+
+        if (!IsValidUnionValue(vtable_entry->second.field->type()->index(),
+                               realized_type.value())) {
+          // We already export an error in the union type field, so just skip
+          // building the union itself and it will default to an unreference
+          // Binary section.
+          offset += sizeof(uint32_t);
+          continue;
+        }
+
+        const std::string enum_type =
+            BuildUnion(union_offset, realized_type.value(), field);
+
+        comment.default_value = "(`" + enum_type + "`)";
+        regions.push_back(MakeBinaryRegion(offset, sizeof(uint32_t),
+                                           BinaryRegionType::UOffset, 0,
+                                           union_offset, comment));
+
+        offset += sizeof(uint32_t);
+      }
+    } break;
+    default: {
+      if (IsScalar(field->type()->element())) {
+        const BinaryRegionType binary_region_type =
+            GetRegionType(field->type()->element());
+
+        const uint64_t type_size = GetTypeSize(field->type()->element());
+
+        // TODO(dbaileychess): It might be nicer to user the
+        // BinaryRegion.array_length field to indicate this.
+        for (size_t i = 0; i < vector_length.value(); ++i) {
+          BinaryRegionComment vector_scalar_comment;
+          vector_scalar_comment.type = BinaryRegionCommentType::VectorValue;
+          vector_scalar_comment.index = i;
+
+          if (!IsValidRead(offset, type_size)) {
+            const uint64_t remaining = RemainingBytes(offset);
+
+            SetError(vector_scalar_comment,
+                     BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
+                     std::to_string(type_size));
+
+            regions.push_back(
+                MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
+                                 remaining, 0, vector_scalar_comment));
+            break;
+          }
+
+          if (IsUnionType(field->type()->element())) {
+            // This is a type for a union. Validate the value
+            const auto enum_value = ReadScalar<uint8_t>(offset);
+
+            // This should always have a value, due to the IsValidRead check
+            // above.
+            if (!IsValidUnionValue(field->type()->index(),
+                                   enum_value.value())) {
+              SetError(vector_scalar_comment,
+                       BinaryRegionStatus::ERROR_INVALID_UNION_TYPE);
+              regions.push_back(MakeBinaryRegion(offset, type_size,
+                                                 binary_region_type, 0, 0,
+                                                 vector_scalar_comment));
+              offset += type_size;
+              continue;
+            }
+          }
+
+          regions.push_back(MakeBinaryRegion(offset, type_size,
+                                             binary_region_type, 0, 0,
+                                             vector_scalar_comment));
+          offset += type_size;
+        }
+      }
+    } break;
+  }
+  AddSection(vector_offset,
+             MakeBinarySection(std::string(table->name()->c_str()) + "." +
+                                   field->name()->c_str(),
+                               BinarySectionType::Vector, std::move(regions)));
+}
+
+std::string BinaryAnnotator::BuildUnion(const uint64_t union_offset,
+                                        const uint8_t realized_type,
+                                        const reflection::Field *const field) {
+  const reflection::Enum *next_enum =
+      schema_->enums()->Get(field->type()->index());
+
+  const reflection::EnumVal *enum_val = next_enum->values()->Get(realized_type);
+
+  if (ContainsSection(union_offset)) { return enum_val->name()->c_str(); }
+
+  const reflection::Type *union_type = enum_val->union_type();
+
+  if (union_type->base_type() == reflection::BaseType::Obj) {
+    const reflection::Object *object =
+        schema_->objects()->Get(union_type->index());
+
+    if (object->is_struct()) {
+      // Union of vectors point to a new Binary section
+      std::vector<BinaryRegion> regions;
+
+      BuildStruct(union_offset, regions, object);
+
+      AddSection(
+          union_offset,
+          MakeBinarySection(std::string(object->name()->c_str()) + "." +
+                                field->name()->c_str(),
+                            BinarySectionType::Union, std::move(regions)));
+    } else {
+      BuildTable(union_offset, BinarySectionType::Table, object);
+    }
+  }
+  // TODO(dbaileychess): handle the other union types.
+
+  return enum_val->name()->c_str();
+}
+
+void BinaryAnnotator::FixMissingRegions() {
+  std::vector<BinaryRegion> regions_to_insert;
+  for (auto &current_section : sections_) {
+    BinarySection &section = current_section.second;
+    if (section.regions.empty()) {
+      // TODO(dbaileychess): is this possible?
+      continue;
+    }
+
+    uint64_t offset = section.regions[0].offset + section.regions[0].length;
+    for (size_t i = 1; i < section.regions.size(); ++i) {
+      BinaryRegion &region = section.regions[i];
+
+      const uint64_t next_offset = region.offset;
+      if (!IsValidOffset(next_offset)) {
+        // TODO(dbaileychess): figure out how we get into this situation.
+        continue;
+      }
+
+      if (offset < next_offset) {
+        const uint64_t padding_bytes = next_offset - offset;
+
+        BinaryRegionComment comment;
+        comment.type = BinaryRegionCommentType::Padding;
+
+        if (IsNonZeroRegion(offset, padding_bytes, binary_)) {
+          SetError(comment, BinaryRegionStatus::WARN_NO_REFERENCES);
+          regions_to_insert.push_back(
+              MakeBinaryRegion(offset, padding_bytes, BinaryRegionType::Unknown,
+                               padding_bytes, 0, comment));
+        } else {
+          regions_to_insert.push_back(
+              MakeBinaryRegion(offset, padding_bytes, BinaryRegionType::Uint8,
+                               padding_bytes, 0, comment));
+        }
+      }
+      offset = next_offset + region.length;
+    }
+
+    if (!regions_to_insert.empty()) {
+      section.regions.insert(section.regions.end(), regions_to_insert.begin(),
+                             regions_to_insert.end());
+      std::stable_sort(section.regions.begin(), section.regions.end(),
+                       BinaryRegionSort);
+      regions_to_insert.clear();
+    }
+  }
+}
+
+void BinaryAnnotator::FixMissingSections() {
+  uint64_t offset = 0;
+
+  std::vector<BinarySection> sections_to_insert;
+
+  for (auto &current_section : sections_) {
+    BinarySection &section = current_section.second;
+    const uint64_t section_start_offset = current_section.first;
+    const uint64_t section_end_offset =
+        section.regions.back().offset + section.regions.back().length;
+
+    if (offset < section_start_offset) {
+      // We are at an offset that is less then the current section.
+      const uint64_t pad_bytes = section_start_offset - offset + 1;
+
+      sections_to_insert.push_back(
+          GenerateMissingSection(offset - 1, pad_bytes, binary_));
+    }
+    offset = section_end_offset + 1;
+  }
+
+  // Handle the case where there are still bytes left in the binary that are
+  // unaccounted for.
+  if (offset < binary_length_) {
+    const uint64_t pad_bytes = binary_length_ - offset + 1;
+    sections_to_insert.push_back(
+        GenerateMissingSection(offset - 1, pad_bytes, binary_));
+  }
+
+  for (const BinarySection &section_to_insert : sections_to_insert) {
+    AddSection(section_to_insert.regions[0].offset, section_to_insert);
+  }
+}
+
+bool BinaryAnnotator::ContainsSection(const uint64_t offset) {
+  auto it = sections_.lower_bound(offset);
+  // If the section is found, check that it is exactly equal its offset.
+  if (it != sections_.end() && it->first == offset) { return true; }
+
+  // If this was the first section, there are no other previous sections to
+  // check.
+  if (it == sections_.begin()) { return false; }
+
+  // Go back one section.
+  --it;
+
+  // And check that if the offset is covered by the section.
+  return offset >= it->first && offset < it->second.regions.back().offset +
+                                             it->second.regions.back().length;
+}
+
+}  // namespace flatbuffers
\ No newline at end of file
diff --git a/src/binary_annotator.h b/src/binary_annotator.h
new file mode 100644
index 0000000..f89d0a9
--- /dev/null
+++ b/src/binary_annotator.h
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2021 Google Inc. All rights reserved.
+ *
+ * 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
+ *
+ *     http://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.
+ */
+
+#ifndef FLATBUFFERS_BINARY_ANNOTATOR_H_
+#define FLATBUFFERS_BINARY_ANNOTATOR_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "flatbuffers/base.h"
+#include "flatbuffers/reflection.h"
+#include "flatbuffers/stl_emulation.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+enum class BinaryRegionType {
+  Unknown = 0,
+  UOffset = 1,
+  SOffset = 2,
+  VOffset = 3,
+  Bool = 4,
+  Byte = 5,
+  Char = 6,
+  Uint8 = 7,
+  Int8 = 8,
+  Uint16 = 9,
+  Int16 = 10,
+  Uint32 = 11,
+  Int32 = 12,
+  Uint64 = 13,
+  Int64 = 14,
+  Float = 15,
+  Double = 16,
+  UType = 17,
+};
+
+template<typename T>
+static inline std::string ToHex(T i, size_t width = sizeof(T)) {
+  std::stringstream stream;
+  stream << std::hex << std::uppercase << std::setfill('0') << std::setw(width)
+         << i;
+  return stream.str();
+}
+
+// Specialized version for uint8_t that don't work well with std::hex.
+static inline std::string ToHex(uint8_t i) {
+  return ToHex(static_cast<int>(i), 2);
+}
+
+enum class BinaryRegionStatus {
+  OK = 0,
+  WARN = 100,
+  WARN_NO_REFERENCES,
+  WARN_CORRUPTED_PADDING,
+  WARN_PADDING_LENGTH,
+  ERROR = 200,
+  // An offset is pointing outside the binary bounds.
+  ERROR_OFFSET_OUT_OF_BINARY,
+  // Expecting to read N bytes but not enough remain in the binary.
+  ERROR_INCOMPLETE_BINARY,
+  // When a length of a vtable/vector is longer than possible.
+  ERROR_LENGTH_TOO_LONG,
+  // When a length of a vtable/vector is shorter than possible.
+  ERROR_LENGTH_TOO_SHORT,
+  // A field mark required is not present in the vtable.
+  ERROR_REQUIRED_FIELD_NOT_PRESENT,
+  // A realized union type is not within the enum bounds.
+  ERROR_INVALID_UNION_TYPE,
+  // Occurs when there is a cycle in offsets.
+  ERROR_CYCLE_DETECTED,
+};
+
+enum class BinaryRegionCommentType {
+  Unknown = 0,
+  SizePrefix,
+  // The offset to the root table.
+  RootTableOffset,
+  // The optional 4-char file identifier.
+  FileIdentifier,
+  // Generic 0-filled padding
+  Padding,
+  // The size of the vtable.
+  VTableSize,
+  // The size of the referring table.
+  VTableRefferingTableLength,
+  // Offsets to vtable fields.
+  VTableFieldOffset,
+  // Offsets to unknown vtable fields.
+  VTableUnknownFieldOffset,
+  // The vtable offset of a table.
+  TableVTableOffset,
+  // A "inline" table field value.
+  TableField,
+  // A table field that is unknown.
+  TableUnknownField,
+  // A table field value that points to another section.
+  TableOffsetField,
+  // A struct field value.
+  StructField,
+  // A array field value.
+  ArrayField,
+  // The length of the string.
+  StringLength,
+  // The string contents.
+  StringValue,
+  // The explicit string terminator.
+  StringTerminator,
+  // The length of the vector (# of items).
+  VectorLength,
+  // A "inline" value of a vector.
+  VectorValue,
+  // A vector value that points to another section.
+  VectorTableValue,
+  VectorStringValue,
+  VectorUnionValue,
+};
+
+struct BinaryRegionComment {
+  BinaryRegionStatus status = BinaryRegionStatus::OK;
+
+  // If status is non OK, this may be filled in with additional details.
+  std::string status_message;
+
+  BinaryRegionCommentType type = BinaryRegionCommentType::Unknown;
+
+  std::string name;
+
+  std::string default_value;
+
+  size_t index = 0;
+};
+
+struct BinaryRegion {
+  // Offset into the binary where this region begins.
+  uint64_t offset = 0;
+
+  // The length of this region in bytes.
+  uint64_t length = 0;
+
+  // The underlying datatype of this region
+  BinaryRegionType type = BinaryRegionType::Unknown;
+
+  // If `type` is an array/vector, this is the number of those types this region
+  // encompasses.
+  uint64_t array_length = 0;
+
+  // If the is an offset to some other region, this is what it points to. The
+  // offset is relative to overall binary, not to this region.
+  uint64_t points_to_offset = 0;
+
+  // The comment on the region.
+  BinaryRegionComment comment;
+};
+
+enum class BinarySectionType {
+  Unknown = 0,
+  Header = 1,
+  Table = 2,
+  RootTable = 3,
+  VTable = 4,
+  Struct = 5,
+  String = 6,
+  Vector = 7,
+  Union = 8,
+  Padding = 9,
+};
+
+// A section of the binary that is grouped together in some logical manner, and
+// often is pointed too by some other offset BinaryRegion. Sections include
+// `tables`, `vtables`, `strings`, `vectors`, etc..
+struct BinarySection {
+  // User-specified name of the section, if applicable.
+  std::string name;
+
+  // The type of this section.
+  BinarySectionType type = BinarySectionType::Unknown;
+
+  // The binary regions that make up this section, in order of their offsets.
+  std::vector<BinaryRegion> regions;
+};
+
+inline static BinaryRegionType GetRegionType(reflection::BaseType base_type) {
+  switch (base_type) {
+    case reflection::UType: return BinaryRegionType::UType;
+    case reflection::Bool: return BinaryRegionType::Uint8;
+    case reflection::Byte: return BinaryRegionType::Uint8;
+    case reflection::UByte: return BinaryRegionType::Uint8;
+    case reflection::Short: return BinaryRegionType::Int16;
+    case reflection::UShort: return BinaryRegionType::Uint16;
+    case reflection::Int: return BinaryRegionType::Uint32;
+    case reflection::UInt: return BinaryRegionType::Uint32;
+    case reflection::Long: return BinaryRegionType::Int64;
+    case reflection::ULong: return BinaryRegionType::Uint64;
+    case reflection::Float: return BinaryRegionType::Float;
+    case reflection::Double: return BinaryRegionType::Double;
+    default: return BinaryRegionType::Unknown;
+  }
+}
+
+inline static std::string ToString(const BinaryRegionType type) {
+  switch (type) {
+    case BinaryRegionType::UOffset: return "UOffset32";
+    case BinaryRegionType::SOffset: return "SOffset32";
+    case BinaryRegionType::VOffset: return "VOffset16";
+    case BinaryRegionType::Bool: return "bool";
+    case BinaryRegionType::Char: return "char";
+    case BinaryRegionType::Byte: return "int8_t";
+    case BinaryRegionType::Uint8: return "uint8_t";
+    case BinaryRegionType::Uint16: return "uint16_t";
+    case BinaryRegionType::Uint32: return "uint32_t";
+    case BinaryRegionType::Uint64: return "uint64_t"; ;
+    case BinaryRegionType::Int8: return "int8_t";
+    case BinaryRegionType::Int16: return "int16_t";
+    case BinaryRegionType::Int32: return "int32_t";
+    case BinaryRegionType::Int64: return "int64_t";
+    case BinaryRegionType::Double: return "double";
+    case BinaryRegionType::Float: return "float";
+    case BinaryRegionType::UType: return "UType8";
+    case BinaryRegionType::Unknown: return "?uint8_t";
+    default: return "todo";
+  }
+}
+
+class BinaryAnnotator {
+ public:
+  explicit BinaryAnnotator(const uint8_t *const bfbs,
+                           const uint64_t bfbs_length,
+                           const uint8_t *const binary,
+                           const uint64_t binary_length)
+      : bfbs_(bfbs),
+        bfbs_length_(bfbs_length),
+        schema_(reflection::GetSchema(bfbs)),
+        binary_(binary),
+        binary_length_(binary_length) {}
+
+  std::map<uint64_t, BinarySection> Annotate();
+
+ private:
+  struct VTable {
+    struct Entry {
+      const reflection::Field *field = nullptr;
+      uint16_t offset_from_table = 0;
+    };
+
+    // Field ID -> {field def, offset from table}
+    std::map<uint16_t, Entry> fields;
+
+    uint16_t vtable_size = 0;
+    uint16_t table_size = 0;
+  };
+
+  uint64_t BuildHeader(uint64_t offset);
+
+  void BuildVTable(uint64_t offset, const reflection::Object *table,
+                   uint64_t offset_of_referring_table);
+
+  void BuildTable(uint64_t offset, const BinarySectionType type,
+                  const reflection::Object *table);
+
+  uint64_t BuildStruct(uint64_t offset, std::vector<BinaryRegion> &regions,
+                       const reflection::Object *structure);
+
+  void BuildString(uint64_t offset, const reflection::Object *table,
+                   const reflection::Field *field);
+
+  void BuildVector(uint64_t offset, const reflection::Object *table,
+                   const reflection::Field *field, uint64_t parent_table_offset,
+                   const VTable &vtable);
+
+  std::string BuildUnion(uint64_t offset, uint8_t realized_type,
+                         const reflection::Field *field);
+
+  void FixMissingRegions();
+  void FixMissingSections();
+
+  inline bool IsValidOffset(const uint64_t offset) const {
+    return offset < binary_length_;
+  }
+
+  // Determines if performing a GetScalar request for `T` at `offset` would read
+  // passed the end of the binary.
+  template<typename T> inline bool IsValidRead(const uint64_t offset) const {
+    return IsValidRead(offset, sizeof(T));
+  }
+
+  inline bool IsValidRead(const uint64_t offset, const uint64_t length) const {
+    return length < binary_length_ && IsValidOffset(offset + length - 1);
+  }
+
+  // Calculate the number of bytes remaining from the given offset. If offset is
+  // > binary_length, 0 is returned.
+  uint64_t RemainingBytes(const uint64_t offset) const {
+    return IsValidOffset(offset) ? binary_length_ - offset : 0;
+  }
+
+  template<typename T>
+  flatbuffers::Optional<T> ReadScalar(const uint64_t offset) const {
+    if (!IsValidRead<T>(offset)) { return flatbuffers::nullopt; }
+
+    return flatbuffers::ReadScalar<T>(binary_ + offset);
+  }
+
+  // Adds the provided `section` keyed by the `offset` it occurs at. If a
+  // section is already added at that offset, it doesn't replace the exisiting
+  // one.
+  void AddSection(const uint64_t offset, const BinarySection &section) {
+    sections_.insert(std::make_pair(offset, section));
+  }
+
+  bool IsInlineField(const reflection::Field *const field) {
+    if (field->type()->base_type() == reflection::BaseType::Obj) {
+      return schema_->objects()->Get(field->type()->index())->is_struct();
+    }
+    return IsScalar(field->type()->base_type());
+  }
+
+  bool IsUnionType(const reflection::BaseType type) {
+    return (type == reflection::BaseType::UType ||
+            type == reflection::BaseType::Union);
+  }
+
+  bool IsUnionType(const reflection::Field *const field) {
+    return IsUnionType(field->type()->base_type()) &&
+           field->type()->index() >= 0;
+  }
+
+  bool IsValidUnionValue(const reflection::Field *const field,
+                         const uint8_t value) {
+    return IsUnionType(field) &&
+           IsValidUnionValue(field->type()->index(), value);
+  }
+
+  bool IsValidUnionValue(const uint32_t enum_id, const uint8_t value) {
+    if (enum_id >= schema_->enums()->size()) { return false; }
+
+    const reflection::Enum *enum_def = schema_->enums()->Get(enum_id);
+
+    if (enum_def == nullptr) { return false; }
+
+    return value < enum_def->values()->size();
+  }
+
+  uint64_t GetElementSize(const reflection::Field *const field) {
+    if (IsScalar(field->type()->element())) {
+      return GetTypeSize(field->type()->element());
+    }
+
+    switch (field->type()->element()) {
+      case reflection::BaseType::Obj: {
+        auto obj = schema_->objects()->Get(field->type()->index());
+        return obj->is_struct() ? obj->bytesize() : sizeof(uint32_t);
+      }
+      default: return sizeof(uint32_t);
+    }
+  }
+
+  bool ContainsSection(const uint64_t offset);
+
+  // The schema for the binary file
+  const uint8_t *bfbs_;
+  const uint64_t bfbs_length_;
+  const reflection::Schema *schema_;
+
+  // The binary data itself.
+  const uint8_t *binary_;
+  const uint64_t binary_length_;
+
+  // Map of binary offset to vtables, to dedupe vtables.
+  std::map<uint64_t, VTable> vtables_;
+
+  // The annotated binary sections, index by their absolute offset.
+  std::map<uint64_t, BinarySection> sections_;
+};
+
+}  // namespace flatbuffers
+
+#endif  // FLATBUFFERS_BINARY_ANNOTATOR_H_
\ No newline at end of file
diff --git a/src/code_generators.cpp b/src/code_generators.cpp
index 269687d..c5efb22 100644
--- a/src/code_generators.cpp
+++ b/src/code_generators.cpp
@@ -30,6 +30,52 @@
 
 namespace flatbuffers {
 
+namespace {
+
+static std::string JavaCSharpMakeRule(const bool java, const Parser &parser,
+                               const std::string &path,
+                               const std::string &file_name) {
+  const std::string file_extension = java ? ".java" : ".cs";
+  std::string make_rule;
+
+  for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end();
+       ++it) {
+    auto &enum_def = **it;
+    if (!make_rule.empty()) make_rule += " ";
+    std::string directory =
+        BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace);
+    make_rule += directory + enum_def.name + file_extension;
+  }
+
+  for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end();
+       ++it) {
+    auto &struct_def = **it;
+    if (!make_rule.empty()) make_rule += " ";
+    std::string directory = BaseGenerator::NamespaceDir(
+        parser, path, *struct_def.defined_namespace);
+    make_rule += directory + struct_def.name + file_extension;
+  }
+
+  make_rule += ": ";
+  auto included_files = parser.GetIncludedFilesRecursive(file_name);
+  for (auto it = included_files.begin(); it != included_files.end(); ++it) {
+    make_rule += " " + *it;
+  }
+  return make_rule;
+}
+
+
+static std::string BinaryFileName(const Parser &parser, const std::string &path,
+                           const std::string &file_name) {
+  auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin";
+  return path + file_name + "." + ext;
+}
+
+} // namespace
+
+
+
+
 void CodeWriter::operator+=(std::string text) {
   if (!ignore_ident_ && !text.empty()) AppendIdent(stream_);
 
@@ -131,8 +177,9 @@
   return qualified_name + name;
 }
 
-std::string BaseGenerator::WrapInNameSpace(const Definition &def) const {
-  return WrapInNameSpace(def.defined_namespace, def.name);
+std::string BaseGenerator::WrapInNameSpace(const Definition &def,
+                                           const std::string &suffix) const {
+  return WrapInNameSpace(def.defined_namespace, def.name + suffix);
 }
 
 std::string BaseGenerator::GetNameSpace(const Definition &def) const {
@@ -299,37 +346,6 @@
   return this->NaN(static_cast<double>(v));
 }
 
-std::string JavaCSharpMakeRule(const bool java, const Parser &parser,
-                               const std::string &path,
-                               const std::string &file_name) {
-  const std::string file_extension = java ? ".java" : ".cs";
-  std::string make_rule;
-
-  for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end();
-       ++it) {
-    auto &enum_def = **it;
-    if (!make_rule.empty()) make_rule += " ";
-    std::string directory =
-        BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace);
-    make_rule += directory + enum_def.name + file_extension;
-  }
-
-  for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end();
-       ++it) {
-    auto &struct_def = **it;
-    if (!make_rule.empty()) make_rule += " ";
-    std::string directory = BaseGenerator::NamespaceDir(
-        parser, path, *struct_def.defined_namespace);
-    make_rule += directory + struct_def.name + file_extension;
-  }
-
-  make_rule += ": ";
-  auto included_files = parser.GetIncludedFilesRecursive(file_name);
-  for (auto it = included_files.begin(); it != included_files.end(); ++it) {
-    make_rule += " " + *it;
-  }
-  return make_rule;
-}
 
 std::string JavaMakeRule(const Parser &parser, const std::string &path,
                          const std::string &file_name) {
@@ -340,12 +356,6 @@
   return JavaCSharpMakeRule(false, parser, path, file_name);
 }
 
-std::string BinaryFileName(const Parser &parser, const std::string &path,
-                           const std::string &file_name) {
-  auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin";
-  return path + file_name + "." + ext;
-}
-
 bool GenerateBinary(const Parser &parser, const std::string &path,
                     const std::string &file_name) {
   if (parser.opts.use_flexbuffers) {
diff --git a/src/flatc.cpp b/src/flatc.cpp
index 3b2ef9f..1d12a17 100644
--- a/src/flatc.cpp
+++ b/src/flatc.cpp
@@ -19,11 +19,13 @@
 #include <list>
 #include <sstream>
 
+#include "annotated_binary_text_gen.h"
+#include "binary_annotator.h"
 #include "flatbuffers/util.h"
 
 namespace flatbuffers {
 
-const char *FLATC_VERSION() { return FLATBUFFERS_VERSION(); }
+static const char *FLATC_VERSION() { return FLATBUFFERS_VERSION(); }
 
 void FlatCompiler::ParseFile(
     flatbuffers::Parser &parser, const std::string &filename,
@@ -84,6 +86,8 @@
   { "", "scoped-enums", "",
     "Use C++11 style scoped and strongly typed enums. Also implies "
     "--no-prefix." },
+  { "", "swift-implementation-only", "",
+    "Adds a @_implementationOnly to swift imports" },
   { "", "gen-inclues", "",
     "(deprecated), this is the default behavior. If the original behavior is "
     "required (no include statements) use --no-includes." },
@@ -203,7 +207,7 @@
     "Used with \"binary\" and \"json\" options, it generates data using "
     "schema-less FlexBuffers." },
   { "", "no-warnings", "", "Inhibit all warnings messages." },
-  { "", "warning-as-errors", "", "Treat all warnings as errors." },
+  { "", "warnings-as-errors", "", "Treat all warnings as errors." },
   { "", "cs-global-alias", "",
     "Prepend \"global::\" to all user generated csharp classes and "
     "structs." },
@@ -215,6 +219,11 @@
     "in JSON, which is unsafe unless checked by a verifier afterwards." },
   { "", "ts-flat-files", "",
     "Only generated one typescript file per .fbs file." },
+  { "", "annotate", "SCHEMA",
+    "Annotate the provided BINARY_FILE with the specified SCHEMA file." },
+  { "", "no-leak-private-annotation", "",
+    "Prevents multiple type of annotations within a Fbs SCHEMA file."
+    "Currently this is required to generate private types in Rust" },
 };
 
 static void AppendTextWrappedString(std::stringstream &ss, std::string &text,
@@ -297,7 +306,7 @@
     ss << ", ";
   }
   ss.seekp(-2, ss.cur);
-  ss << "]... FILE... [-- FILE...]";
+  ss << "]... FILE... [-- BINARY_FILE...]";
   std::string help = ss.str();
   std::stringstream ss_textwrap;
   AppendTextWrappedString(ss_textwrap, help, 80, 0);
@@ -306,7 +315,8 @@
 
 std::string FlatCompiler::GetUsageString(const char *program_name) const {
   std::stringstream ss;
-  ss << "Usage: " << program_name << " [OPTION]... FILE... [-- FILE...]\n";
+  ss << "Usage: " << program_name
+     << " [OPTION]... FILE... [-- BINARY_FILE...]\n";
   for (size_t i = 0; i < params_.num_generators; ++i) {
     const Generator &g = params_.generators[i];
     AppendOption(ss, g.option, 80, 25);
@@ -320,16 +330,48 @@
 
   std::string files_description =
       "FILEs may be schemas (must end in .fbs), binary schemas (must end in "
-      ".bfbs) or JSON files (conforming to preceding schema). FILEs after the "
-      "-- must be binary flatbuffer format files. Output files are named using "
-      "the base file name of the input, and written to the current directory "
-      "or the path given by -o. example: " +
+      ".bfbs) or JSON files (conforming to preceding schema). BINARY_FILEs "
+      "after the -- must be binary flatbuffer format files. Output files are "
+      "named using the base file name of the input, and written to the current "
+      "directory or the path given by -o. example: " +
       std::string(program_name) + " -c -b schema1.fbs schema2.fbs data.json";
   AppendTextWrappedString(ss, files_description, 80, 0);
   ss << "\n";
   return ss.str();
 }
 
+void FlatCompiler::AnnotateBinaries(
+    const uint8_t *binary_schema, const uint64_t binary_schema_size,
+    const std::string &schema_filename,
+    const std::vector<std::string> &binary_files) {
+  for (const std::string &filename : binary_files) {
+    std::string binary_contents;
+    if (!flatbuffers::LoadFile(filename.c_str(), true, &binary_contents)) {
+      Warn("unable to load binary file: " + filename);
+      continue;
+    }
+
+    const uint8_t *binary =
+        reinterpret_cast<const uint8_t *>(binary_contents.c_str());
+    const size_t binary_size = binary_contents.size();
+
+    flatbuffers::BinaryAnnotator binary_annotator(
+        binary_schema, binary_schema_size, binary, binary_size);
+
+    auto annotations = binary_annotator.Annotate();
+
+    // TODO(dbaileychess): Right now we just support a single text-based
+    // output of the annotated binary schema, which we generate here. We
+    // could output the raw annotations instead and have third-party tools
+    // use them to generate their own output.
+    flatbuffers::AnnotatedBinaryTextGenerator text_generator(
+        flatbuffers::AnnotatedBinaryTextGenerator::Options{}, annotations,
+        binary, binary_size);
+
+    text_generator.Generate(filename, schema_filename);
+  }
+}
+
 int FlatCompiler::Compile(int argc, const char **argv) {
   if (params_.generators == nullptr || params_.num_generators == 0) {
     return 0;
@@ -353,6 +395,7 @@
   std::vector<bool> generator_enabled(params_.num_generators, false);
   size_t binary_files_from = std::numeric_limits<size_t>::max();
   std::string conform_to_schema;
+  std::string annotate_schema;
 
   const char *program_name = argv[0];
 
@@ -390,7 +433,7 @@
         opts.include_prefix = flatbuffers::ConCatPathFileName(
             flatbuffers::PosixPath(argv[argi]), "");
       } else if (arg == "--keep-prefix") {
-        opts.keep_include_path = true;
+        opts.keep_prefix = true;
       } else if (arg == "--strict-json") {
         opts.strict_json = true;
       } else if (arg == "--allow-non-utf8") {
@@ -452,6 +495,8 @@
         opts.java_checkerframework = true;
       } else if (arg == "--gen-generated") {
         opts.gen_generated = true;
+      } else if (arg == "--swift-implementation-only") {
+        opts.swift_implementation_only = true;
       } else if (arg == "--gen-json-emit") {
         opts.gen_json_coders = true;
       } else if (arg == "--object-prefix") {
@@ -554,6 +599,11 @@
         opts.json_nested_legacy_flatbuffers = true;
       } else if (arg == "--ts-flat-files") {
         opts.ts_flat_file = true;
+      } else if (arg == "--no-leak-private-annotation") {
+        opts.no_leak_private_annotations = true;
+      } else if (arg == "--annotate") {
+        if (++argi >= argc) Error("missing path following: " + arg, true);
+        annotate_schema = flatbuffers::PosixPath(argv[argi]);
       } else {
         for (size_t i = 0; i < params_.num_generators; ++i) {
           if (arg == "--" + params_.generators[i].option.long_opt ||
@@ -582,7 +632,8 @@
   if (opts.proto_mode) {
     if (any_generator)
       Error("cannot generate code directly from .proto files", true);
-  } else if (!any_generator && conform_to_schema.empty()) {
+  } else if (!any_generator && conform_to_schema.empty() &&
+             annotate_schema.empty()) {
     Error("no options: specify at least one generator.", true);
   }
 
@@ -592,10 +643,6 @@
         "well.");
   }
 
-  if (opts.ts_flat_file && opts.generate_all) {
-    Error("Combining --ts-flat-file and --gen-all is not supported.");
-  }
-
   flatbuffers::Parser conform_parser;
   if (!conform_to_schema.empty()) {
     std::string contents;
@@ -611,6 +658,53 @@
     }
   }
 
+  if (!annotate_schema.empty()) {
+    const std::string ext = flatbuffers::GetExtension(annotate_schema);
+    if (!(ext == reflection::SchemaExtension() || ext == "fbs")) {
+      Error("Expected a `.bfbs` or `.fbs` schema, got: " + annotate_schema);
+    }
+
+    const bool is_binary_schema = ext == reflection::SchemaExtension();
+
+    std::string schema_contents;
+    if (!flatbuffers::LoadFile(annotate_schema.c_str(),
+                               /*binary=*/is_binary_schema, &schema_contents)) {
+      Error("unable to load schema: " + annotate_schema);
+    }
+
+    const uint8_t *binary_schema = nullptr;
+    uint64_t binary_schema_size = 0;
+
+    IDLOptions binary_opts;
+    binary_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
+    flatbuffers::Parser parser(binary_opts);
+
+    if (is_binary_schema) {
+      binary_schema =
+          reinterpret_cast<const uint8_t *>(schema_contents.c_str());
+      binary_schema_size = schema_contents.size();
+    } else {
+      // If we need to generate the .bfbs file from the provided schema file
+      // (.fbs)
+      ParseFile(parser, annotate_schema, schema_contents, include_directories);
+      parser.Serialize();
+
+      binary_schema = parser.builder_.GetBufferPointer();
+      binary_schema_size = parser.builder_.GetSize();
+    }
+
+    if (binary_schema == nullptr || !binary_schema_size) {
+      Error("could not parse a value binary schema from: " + annotate_schema);
+    }
+
+    // Annotate the provided files with the binary_schema.
+    AnnotateBinaries(binary_schema, binary_schema_size, annotate_schema,
+                     filenames);
+
+    // We don't support doing anything else after annotating a binary.
+    return 0;
+  }
+
   std::unique_ptr<flatbuffers::Parser> parser(new flatbuffers::Parser(opts));
 
   for (auto file_it = filenames.begin(); file_it != filenames.end();
@@ -755,7 +849,7 @@
           if (params_.generators[i].generateGRPC != nullptr) {
             if (!params_.generators[i].generateGRPC(*parser.get(), output_path,
                                                     filebase)) {
-              Error(std::string("Unable to generate GRPC interface for") +
+              Error(std::string("Unable to generate GRPC interface for ") +
                     params_.generators[i].lang_name);
             }
           } else {
diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp
index abcab24..b4c4251 100644
--- a/src/flatc_main.cpp
+++ b/src/flatc_main.cpp
@@ -52,9 +52,6 @@
 }  // namespace flatbuffers
 
 int main(int argc, const char *argv[]) {
-  // Prevent Appveyor-CI hangs.
-  flatbuffers::SetupDefaultCRTReportMode();
-
   const std::string flatbuffers_version(flatbuffers::FLATBUFFERS_VERSION());
 
   std::unique_ptr<flatbuffers::BfbsGenerator> bfbs_gen_lua =
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index dfd9525..dfe55e0 100644
--- a/src/idl_gen_cpp.cpp
+++ b/src/idl_gen_cpp.cpp
@@ -16,8 +16,10 @@
 
 // independent from idl_parser, since this code is not needed for most clients
 
+#include <string>
 #include <unordered_set>
 
+#include "flatbuffers/base.h"
 #include "flatbuffers/code_generators.h"
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/flatc.h"
@@ -68,6 +70,19 @@
   return guard;
 }
 
+static bool IsVectorOfPointers(const FieldDef &field) {
+  const auto &type = field.value.type;
+  const auto &vector_type = type.VectorType();
+  return type.base_type == BASE_TYPE_VECTOR &&
+         vector_type.base_type == BASE_TYPE_STRUCT &&
+         !vector_type.struct_def->fixed && !field.native_inline;
+}
+
+static bool IsPointer(const FieldDef &field) {
+  return field.value.type.base_type == BASE_TYPE_STRUCT &&
+         !IsStruct(field.value.type);
+}
+
 namespace cpp {
 
 enum CppStandard { CPP_STD_X0 = 0, CPP_STD_11, CPP_STD_17 };
@@ -199,27 +214,60 @@
     for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
   }
 
+  // Adds code to check that the included flatbuffers.h is of the same version
+  // as the generated code. This check currently looks for exact version match,
+  // as we would guarantee that they are compatible, but in theory a newer
+  // version of flatbuffers.h should work with a old code gen if we do proper
+  // backwards support.
+  void GenFlatbuffersVersionCheck() {
+    code_ +=
+        "// Ensure the included flatbuffers.h is the same version as when this "
+        "file was";
+    code_ += "// generated, otherwise it may not be compatible.";
+    code_ += "static_assert(FLATBUFFERS_VERSION_MAJOR == " +
+             std::to_string(FLATBUFFERS_VERSION_MAJOR) + " &&";
+    code_ += "              FLATBUFFERS_VERSION_MINOR == " +
+             std::to_string(FLATBUFFERS_VERSION_MINOR) + " &&";
+    code_ += "              FLATBUFFERS_VERSION_REVISION == " +
+             std::to_string(FLATBUFFERS_VERSION_REVISION) + ",";
+    code_ += "             \"Non-compatible flatbuffers version included\");";
+  }
+
   void GenIncludeDependencies() {
-    int num_includes = 0;
     if (opts_.generate_object_based_api) {
       for (auto it = parser_.native_included_files_.begin();
            it != parser_.native_included_files_.end(); ++it) {
         code_ += "#include \"" + *it + "\"";
-        num_includes++;
       }
     }
-    for (auto it = parser_.included_files_.begin();
-         it != parser_.included_files_.end(); ++it) {
-      if (it->second.empty()) continue;
-      auto noext = flatbuffers::StripExtension(it->second);
-      auto basename = flatbuffers::StripPath(noext);
-      auto includeName =
-          GeneratedFileName(opts_.include_prefix,
-                            opts_.keep_include_path ? noext : basename, opts_);
-      code_ += "#include \"" + includeName + "\"";
-      num_includes++;
+
+    // Get the directly included file of the file being parsed.
+    std::vector<IncludedFile> included_files(parser_.GetIncludedFiles());
+
+    // We are safe to sort them alphabetically, since there shouldn't be any
+    // interdependence between them.
+    std::stable_sort(included_files.begin(), included_files.end());
+
+    for (const IncludedFile &included_file : included_files) {
+      // Get the name of the included file as defined by the schema, and strip
+      // the .fbs extension.
+      const std::string name_without_ext =
+          flatbuffers::StripExtension(included_file.schema_name);
+
+      // If we are told to keep the prefix of the included schema, leave it
+      // unchanged, otherwise strip the leading path off so just the "basename"
+      // of the include is retained.
+      const std::string basename =
+          opts_.keep_prefix ? name_without_ext
+                            : flatbuffers::StripPath(name_without_ext);
+
+      code_ += "#include \"" +
+               GeneratedFileName(opts_.include_prefix, basename, opts_) + "\"";
     }
-    if (num_includes) code_ += "";
+
+    if (!parser_.native_included_files_.empty() || !included_files.empty()) {
+      code_ += "";
+    }
   }
 
   void GenExtraIncludes() {
@@ -285,6 +333,8 @@
 
       code_ += "#include \"flatbuffers/flatbuffers.h\"";
       code_ += "";
+      GenFlatbuffersVersionCheck();
+      code_ += "";
 
       SetNameSpace(struct_def.defined_namespace);
       auto name = Name(struct_def);
@@ -347,8 +397,11 @@
     code_ += "#include \"flatbuffers/flatbuffers.h\"";
     if (parser_.uses_flexbuffers_) {
       code_ += "#include \"flatbuffers/flexbuffers.h\"";
+      code_ += "#include \"flatbuffers/flex_flat_util.h\"";
     }
     code_ += "";
+    GenFlatbuffersVersionCheck();
+    code_ += "";
 
     if (opts_.include_dependence_headers) { GenIncludeDependencies(); }
     GenExtraIncludes();
@@ -841,7 +894,9 @@
           }
         } else {
           const auto nn = WrapNativeNameInNameSpace(*type.struct_def, opts_);
-          return forcopy ? nn : GenTypeNativePtr(nn, &field, false);
+          return (forcopy || field.native_inline)
+                     ? nn
+                     : GenTypeNativePtr(nn, &field, false);
         }
       }
       case BASE_TYPE_UNION: {
@@ -1827,9 +1882,8 @@
         if (vec_type.base_type == BASE_TYPE_UTYPE) continue;
         const auto cpp_type = field.attributes.Lookup("cpp_type");
         const auto cpp_ptr_type = field.attributes.Lookup("cpp_ptr_type");
-        const bool is_ptr =
-            (vec_type.base_type == BASE_TYPE_STRUCT && !IsStruct(vec_type)) ||
-            (cpp_type && cpp_ptr_type->constant != "naked");
+        const bool is_ptr = IsVectorOfPointers(field) ||
+                            (cpp_type && cpp_ptr_type->constant != "naked");
         if (is_ptr) { return true; }
       }
     }
@@ -1953,9 +2007,8 @@
                                    ? cpp_type->constant
                                    : GenTypeNative(vec_type, /*invector*/ true,
                                                    field, /*forcopy*/ true);
-        const bool is_ptr =
-            (vec_type.base_type == BASE_TYPE_STRUCT && !IsStruct(vec_type)) ||
-            (cpp_type && cpp_ptr_type->constant != "naked");
+        const bool is_ptr = IsVectorOfPointers(field) ||
+                            (cpp_type && cpp_ptr_type->constant != "naked");
         CodeWriter cw("  ");
         cw.SetValue("FIELD", Name(field));
         cw.SetValue("TYPE", type_name);
@@ -2020,19 +2073,39 @@
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
       const auto &field = **it;
+      const auto accessor = Name(field) + accessSuffix;
+      const auto lhs_accessor = "lhs." + accessor;
+      const auto rhs_accessor = "rhs." + accessor;
+
       if (!field.deprecated &&  // Deprecated fields won't be accessible.
           field.value.type.base_type != BASE_TYPE_UTYPE &&
           (field.value.type.base_type != BASE_TYPE_VECTOR ||
            field.value.type.element != BASE_TYPE_UTYPE)) {
         if (!compare_op.empty()) { compare_op += " &&\n      "; }
-        auto accessor = Name(field) + accessSuffix;
         if (struct_def.fixed || field.native_inline ||
             field.value.type.base_type != BASE_TYPE_STRUCT) {
-          compare_op += "(lhs." + accessor + " == rhs." + accessor + ")";
+          // If the field is a vector of tables, the table need to be compared
+          // by value, instead of by the default unique_ptr == operator which
+          // compares by address.
+          if (IsVectorOfPointers(field)) {
+            const auto type =
+                GenTypeNative(field.value.type.VectorType(), true, field);
+            const auto equal_length =
+                lhs_accessor + ".size() == " + rhs_accessor + ".size()";
+            const auto elements_equal =
+                "std::equal(" + lhs_accessor + ".cbegin(), " + lhs_accessor +
+                ".cend(), " + rhs_accessor + ".cbegin(), [](" + type +
+                " const &a, " + type +
+                " const &b) { return (a == b) || (a && b && *a == *b); })";
+
+            compare_op += "(" + equal_length + " && " + elements_equal + ")";
+          } else {
+            compare_op += "(" + lhs_accessor + " == " + rhs_accessor + ")";
+          }
         } else {
           // Deep compare of std::unique_ptr. Null is not equal to empty.
           std::string both_null =
-              "(lhs." + accessor + " == rhs." + accessor + ")";
+              "(" + lhs_accessor + " == " + rhs_accessor + ")";
           std::string not_null_and_equal = "(lhs." + accessor + " && rhs." +
                                            accessor + " && *lhs." + accessor +
                                            " == *rhs." + accessor + ")";
@@ -2248,7 +2321,7 @@
 
       // @TODO: Mby make this decisions more universal? How?
       code_.SetValue("U_GET_TYPE",
-                     EscapeKeyword(field.name + UnionTypeFieldSuffix()));
+                     EscapeKeyword(Name(field) + UnionTypeFieldSuffix()));
       code_.SetValue("U_ELEMENT_TYPE", WrapInNameSpace(u->defined_namespace,
                                                        GetEnumValUse(*u, ev)));
       code_.SetValue("U_FIELD_TYPE", "const " + full_struct_name + " *");
@@ -2913,7 +2986,8 @@
             return ptype + "(new " + name + "(*" + val + "))";
           }
         } else {
-          const auto ptype = GenTypeNativePtr(
+          std::string ptype = afield.native_inline ? "*" : "";
+          ptype += GenTypeNativePtr(
               WrapNativeNameInNameSpace(*type.struct_def, opts_), &afield,
               true);
           return ptype + "(" + val + "->UnPack(_resolver))";
@@ -2941,7 +3015,8 @@
         if (field.value.type.element == BASE_TYPE_UTYPE) {
           name = StripUnionType(Name(field));
         }
-        code += "{ _o->" + name + ".resize(_e->size()); ";
+        const std::string vector_field = "_o->" + name;
+        code += "{ " + vector_field + ".resize(_e->size()); ";
         if (!field.value.type.enum_def && !IsBool(field.value.type.element) &&
             IsOneByte(field.value.type.element)) {
           // For vectors of bytes, std::copy is used to improve performance.
@@ -2981,7 +3056,7 @@
             //    (*resolver)(&_o->field, (hash_value_t)(_e));
             //  else
             //    _o->field = nullptr;
-            code += "//vector resolver, " + PtrType(&field) + "\n";
+            code += "/*vector resolver, " + PtrType(&field) + "*/ ";
             code += "if (_resolver) ";
             code += "(*_resolver)";
             code += "(reinterpret_cast<void **>(&_o->" + name + "[_i]" +
@@ -2998,9 +3073,7 @@
               code += "/* else do nothing */";
             }
           } else {
-            const bool is_pointer =
-                field.value.type.VectorType().base_type == BASE_TYPE_STRUCT &&
-                !IsStruct(field.value.type.VectorType());
+            const bool is_pointer = IsVectorOfPointers(field);
             if (is_pointer) {
               code += "if(_o->" + name + "[_i]" + ") { ";
               code += indexing + "->UnPackTo(_o->" + name +
@@ -3012,7 +3085,7 @@
                                  field);
             if (is_pointer) { code += "; }"; }
           }
-          code += "; } }";
+          code += "; } } else { " + vector_field + ".resize(0); }";
         }
         break;
       }
@@ -3040,7 +3113,7 @@
           //    (*resolver)(&_o->field, (hash_value_t)(_e));
           //  else
           //    _o->field = nullptr;
-          code += "//scalar resolver, " + PtrType(&field) + " \n";
+          code += "/*scalar resolver, " + PtrType(&field) + "*/ ";
           code += "if (_resolver) ";
           code += "(*_resolver)";
           code += "(reinterpret_cast<void **>(&_o->" + Name(field) + "), ";
@@ -3057,17 +3130,21 @@
         } else {
           // Generate code for assigning the value, of the form:
           //  _o->field = value;
-          const bool is_pointer =
-              field.value.type.base_type == BASE_TYPE_STRUCT &&
-              !IsStruct(field.value.type);
+          const bool is_pointer = IsPointer(field);
+
+          const std::string out_field = "_o->" + Name(field);
+
           if (is_pointer) {
-            code += "{ if(_o->" + Name(field) + ") { ";
-            code += "_e->UnPackTo(_o->" + Name(field) + ".get(), _resolver);";
+            code += "{ if(" + out_field + ") { ";
+            code += "_e->UnPackTo(" + out_field + ".get(), _resolver);";
             code += " } else { ";
           }
-          code += "_o->" + Name(field) + " = ";
+          code += out_field + " = ";
           code += GenUnpackVal(field.value.type, "_e", false, field) + ";";
-          if (is_pointer) { code += " } }"; }
+          if (is_pointer) {
+            code += " } } else if (" + out_field + ") { " + out_field +
+                    ".reset(); }";
+          }
         }
         break;
       }
@@ -3173,9 +3250,13 @@
               code += "(" + value + ".size(), ";
               code += "[](size_t i, _VectorArgs *__va) { ";
               code += "return Create" + vector_type.struct_def->name;
-              code += "(*__va->__fbb, __va->_" + value + "[i]" +
-                      GenPtrGet(field) + ", ";
-              code += "__va->__rehasher); }, &_va )";
+              code += "(*__va->__fbb, ";
+              if (field.native_inline) {
+                code += "&(__va->_" + value + "[i])";
+              } else {
+                code += "__va->_" + value + "[i]" + GenPtrGet(field);
+              }
+              code += ", __va->__rehasher); }, &_va )";
             }
             break;
           }
@@ -3234,7 +3315,8 @@
         // If set_empty_vectors_to_null option is enabled, for optional fields,
         // check to see if there actually is any data in _o->field before
         // attempting to access it.
-        if (opts_.set_empty_vectors_to_null && !field.IsRequired()) {
+        if (field.attributes.Lookup("nested_flatbuffer") ||
+            (opts_.set_empty_vectors_to_null && !field.IsRequired())) {
           code = value + ".size() ? " + code + " : 0";
         }
         break;
@@ -3257,14 +3339,15 @@
           } else if (field.native_inline) {
             code += "&" + value;
           } else {
-            code += value + " ? " + value + GenPtrGet(field) + " : 0";
+            code += value + " ? " + value + GenPtrGet(field) + " : nullptr";
           }
         } else {
           // _o->field ? CreateT(_fbb, _o->field.get(), _rehasher);
           const auto type = field.value.type.struct_def->name;
           code += value + " ? Create" + type;
-          code += "(_fbb, " + value + GenPtrGet(field) + ", _rehasher)";
-          code += " : 0";
+          code += "(_fbb, " + value;
+          if (!field.native_inline) code += GenPtrGet(field);
+          code += ", _rehasher) : 0";
         }
         break;
       }
@@ -3441,7 +3524,7 @@
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
       const auto field = *it;
-      const auto field_name = field->name + "_";
+      const auto field_name = Name(*field) + "_";
 
       if (first_in_init_list) {
         first_in_init_list = false;
diff --git a/src/idl_gen_csharp.cpp b/src/idl_gen_csharp.cpp
index cb605de..fa67eaf 100644
--- a/src/idl_gen_csharp.cpp
+++ b/src/idl_gen_csharp.cpp
@@ -226,7 +226,7 @@
     if (needs_includes) {
       code += "using global::System;\n";
       code += "using global::System.Collections.Generic;\n";
-      code += "using global::FlatBuffers;\n\n";
+      code += "using global::Google.FlatBuffers;\n\n";
     }
     code += classcode;
     if (!namespace_name.empty()) { code += "\n}\n"; }
@@ -459,20 +459,24 @@
     }
   }
 
+  std::string GetObjectConstructor(flatbuffers::StructDef &struct_def,
+                                   const std::string &data_buffer,
+                                   const std::string &offset) const {
+    // Use the generated type directly, to properly handle default values that
+    // might not be written to the buffer.
+    return "new " + Name(struct_def) + "().__assign(" + offset + ", " +
+           data_buffer + ")";
+  }
+
   // Returns the function name that is able to read a value of the given type.
-  std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field,
+  std::string GenGetterForLookupByKey(flatbuffers::StructDef &struct_def,
+                                      flatbuffers::FieldDef *key_field,
                                       const std::string &data_buffer,
-                                      const char *num = nullptr) const {
-    auto type = key_field->value.type;
-    auto dest_mask = "";
-    auto dest_cast = DestinationCast(type);
-    auto getter = data_buffer + ".Get";
-    if (GenTypeBasic(type, false) != "byte") {
-      getter += ConvertCase(GenTypeBasic(type, false), Case::kUpperCamel);
-    }
-    getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" +
-             dest_mask;
-    return getter;
+                                      const std::string &offset) const {
+    // Use the generated type directly, to properly handle default values that
+    // might not be written to the buffer.
+    return GetObjectConstructor(struct_def, data_buffer, offset) + "." +
+           Name(*key_field);
   }
 
   // Direct mutation is only allowed for scalar fields.
@@ -605,37 +609,15 @@
     return key_offset;
   }
 
-  std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const {
-    std::string key_getter = "      ";
-    key_getter += "int tableOffset = Table.";
-    key_getter += "__indirect(vectorLocation + 4 * (start + middle)";
-    key_getter += ", bb);\n      ";
-    if (IsString(key_field->value.type)) {
-      key_getter += "int comp = Table.";
-      key_getter += "CompareStrings(";
-      key_getter += GenOffsetGetter(key_field);
-      key_getter += ", byteKey, bb);\n";
-    } else {
-      auto get_val = GenGetterForLookupByKey(key_field, "bb");
-      key_getter += "int comp = " + get_val + ".CompareTo(key);\n";
-    }
-    return key_getter;
-  }
-
-  std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const {
-    std::string key_getter = "";
-    auto data_buffer = "builder.DataBuffer";
-    if (IsString(key_field->value.type)) {
-      key_getter += "Table.CompareStrings(";
-      key_getter += GenOffsetGetter(key_field, "o1") + ", ";
-      key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")";
-    } else {
-      auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1");
-      key_getter += field_getter;
-      field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
-      key_getter += ".CompareTo(" + field_getter + ")";
-    }
-    return key_getter;
+  std::string GenKeyGetter(flatbuffers::StructDef &struct_def,
+                           flatbuffers::FieldDef *key_field) const {
+    // Get the getter for the key of the struct.
+    return GenGetterForLookupByKey(struct_def, key_field, "builder.DataBuffer",
+                                   "builder.DataBuffer.Length - o1.Value") +
+           ".CompareTo(" +
+           GenGetterForLookupByKey(struct_def, key_field, "builder.DataBuffer",
+                                   "builder.DataBuffer.Length - o2.Value") +
+           ")";
   }
 
   void GenStruct(StructDef &struct_def, std::string *code_ptr,
@@ -669,11 +651,11 @@
     code += "  public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
 
     if (!struct_def.fixed) {
-      // Generate verson check method.
+      // Generate version check method.
       // Force compile time error if not using the same version runtime.
       code += "  public static void ValidateVersion() {";
       code += " FlatBufferConstants.";
-      code += "FLATBUFFERS_2_0_0(); ";
+      code += "FLATBUFFERS_2_0_8(); ";
       code += "}\n";
 
       // Generate a special accessor for the table that when used as the root
@@ -914,7 +896,9 @@
       code += member_suffix;
       code += "}\n";
       if (IsVector(field.value.type)) {
-        code += "  public int " + Name(field);
+        auto camel_name = Name(field);
+        if (camel_name == struct_def.name) { camel_name += "_"; }
+        code += "  public int " + camel_name;
         code += "Length";
         code += " { get";
         code += offset_prefix;
@@ -1238,6 +1222,8 @@
             code += "); return ";
             code += "builder.EndVector(); }\n";
 
+            // add Create...VectorBlock() overloads for T[], ArraySegment<T> and
+            // IntPtr
             code += "  public static VectorOffset ";
             code += "Create";
             code += Name(field);
@@ -1248,6 +1234,26 @@
             code += ", data.Length, ";
             code += NumToString(alignment);
             code += "); builder.Add(data); return builder.EndVector(); }\n";
+
+            code += "  public static VectorOffset ";
+            code += "Create";
+            code += Name(field);
+            code += "VectorBlock(FlatBufferBuilder builder, ";
+            code += "ArraySegment<" + GenTypeBasic(vector_type) + "> data) ";
+            code += "{ builder.StartVector(";
+            code += NumToString(elem_size);
+            code += ", data.Count, ";
+            code += NumToString(alignment);
+            code += "); builder.Add(data); return builder.EndVector(); }\n";
+
+            code += "  public static VectorOffset ";
+            code += "Create";
+            code += Name(field);
+            code += "VectorBlock(FlatBufferBuilder builder, ";
+            code += "IntPtr dataPtr, int sizeInBytes) ";
+            code += "{ builder.StartVector(1, sizeInBytes, 1); ";
+            code += "builder.Add<" + GenTypeBasic(vector_type) +
+                    ">(dataPtr, sizeInBytes); return builder.EndVector(); }\n";
           }
           // Generate a method to start a vector, data to be added manually
           // after.
@@ -1300,9 +1306,10 @@
       code += "(FlatBufferBuilder builder, ";
       code += "Offset<" + struct_def.name + ">";
       code += "[] offsets) {\n";
-      code += "    Array.Sort(offsets, (Offset<" + struct_def.name +
-              "> o1, Offset<" + struct_def.name + "> o2) => " +
-              GenKeyGetter(key_field);
+      code += "    Array.Sort(offsets,\n";
+      code += "      (Offset<" + struct_def.name + "> o1, Offset<" +
+              struct_def.name + "> o2) =>\n";
+      code += "        " + GenKeyGetter(struct_def, key_field);
       code += ");\n";
       code += "    return builder.CreateVectorOfTables(offsets);\n  }\n";
 
@@ -1311,16 +1318,20 @@
       code += "int vectorLocation, ";
       code += GenTypeGet(key_field->value.type);
       code += " key, ByteBuffer bb) {\n";
-      if (IsString(key_field->value.type)) {
-        code += "    byte[] byteKey = ";
-        code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
-      }
+      code +=
+          "    " + struct_def.name + " obj_ = new " + struct_def.name + "();\n";
       code += "    int span = ";
       code += "bb.GetInt(vectorLocation - 4);\n";
       code += "    int start = 0;\n";
       code += "    while (span != 0) {\n";
       code += "      int middle = span / 2;\n";
-      code += GenLookupKeyGetter(key_field);
+      code +=
+          "      int tableOffset = Table.__indirect(vectorLocation + 4 * "
+          "(start + middle), bb);\n";
+
+      code += "      obj_.__assign(tableOffset, bb);\n";
+      code +=
+          "      int comp = obj_." + Name(*key_field) + ".CompareTo(key);\n";
       code += "      if (comp > 0) {\n";
       code += "        span = middle;\n";
       code += "      } else if (comp < 0) {\n";
@@ -1328,9 +1339,7 @@
       code += "        start += middle;\n";
       code += "        span -= middle;\n";
       code += "      } else {\n";
-      code += "        return ";
-      code += "new " + struct_def.name + "()";
-      code += ".__assign(tableOffset, bb);\n";
+      code += "        return obj_;\n";
       code += "      }\n    }\n";
       code += "    return null;\n";
       code += "  }\n";
@@ -1454,8 +1463,10 @@
     }
     code += "\n";
     // Pack()
-    code += "  public static int Pack(FlatBuffers.FlatBufferBuilder builder, " +
-            union_name + " _o) {\n";
+    code +=
+        "  public static int Pack(Google.FlatBuffers.FlatBufferBuilder "
+        "builder, " +
+        union_name + " _o) {\n";
     code += "    switch (_o.Type) {\n";
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       auto &ev = **it;
@@ -1566,6 +1577,7 @@
 
   void GenUnionUnPack_ObjectAPI(const EnumDef &enum_def, std::string *code_ptr,
                                 const std::string &camel_name,
+                                const std::string &camel_name_short,
                                 bool is_vector) const {
     auto &code = *code_ptr;
     std::string varialbe_name = "_o." + camel_name;
@@ -1584,10 +1596,10 @@
       code += indent + varialbe_name + " = new ";
     }
     code += NamespacedName(enum_def) + "Union();\n";
-    code += indent + varialbe_name + ".Type = this." + camel_name + "Type" +
-            type_suffix + ";\n";
-    code +=
-        indent + "switch (this." + camel_name + "Type" + type_suffix + ") {\n";
+    code += indent + varialbe_name + ".Type = this." + camel_name_short +
+            "Type" + type_suffix + ";\n";
+    code += indent + "switch (this." + camel_name_short + "Type" + type_suffix +
+            ") {\n";
     for (auto eit = enum_def.Vals().begin(); eit != enum_def.Vals().end();
          ++eit) {
       auto &ev = **eit;
@@ -1633,6 +1645,8 @@
       auto &field = **it;
       if (field.deprecated) continue;
       auto camel_name = Name(field);
+      if (camel_name == struct_def.name) { camel_name += "_"; }
+      auto camel_name_short = Name(field);
       auto start = "    _o." + camel_name + " = ";
       switch (field.value.type.base_type) {
         case BASE_TYPE_STRUCT: {
@@ -1666,7 +1680,7 @@
             code += "    for (var _j = 0; _j < this." + camel_name +
                     "Length; ++_j) {\n";
             GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
-                                     camel_name, true);
+                                     camel_name, camel_name_short, true);
             code += "    }\n";
           } else if (field.value.type.element != BASE_TYPE_UTYPE) {
             auto fixed = field.value.type.struct_def == nullptr;
@@ -1687,7 +1701,7 @@
         case BASE_TYPE_UTYPE: break;
         case BASE_TYPE_UNION: {
           GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
-                                   camel_name, false);
+                                   camel_name, camel_name_short, false);
           break;
         }
         default: {
@@ -1707,6 +1721,8 @@
       auto &field = **it;
       if (field.deprecated) continue;
       auto camel_name = Name(field);
+      if (camel_name == struct_def.name) { camel_name += "_"; }
+      auto camel_name_short = Name(field);
       // pre
       switch (field.value.type.base_type) {
         case BASE_TYPE_STRUCT: {
@@ -1782,7 +1798,7 @@
               code += "      var " + array_name + " = _o." + property_name +
                       ".ToArray();\n";
             }
-            code += "      _" + field.name + " = Create" + camel_name +
+            code += "      _" + field.name + " = Create" + camel_name_short +
                     "Vector(builder, " + array_name + ");\n";
             code += "    }\n";
           } else {
@@ -1794,7 +1810,7 @@
                           camel_name + "[_j]);";
             code += "    var _" + field.name + " = default(VectorOffset);\n";
             code += "    if (_o." + camel_name + " != null) {\n";
-            code += "      Start" + camel_name + "Vector(builder, _o." +
+            code += "      Start" + camel_name_short + "Vector(builder, _o." +
                     camel_name + ".Count);\n";
             code += "      for (var _j = _o." + camel_name +
                     ".Count - 1; _j >= 0; --_j) { " + pack_method + " }\n";
@@ -1840,6 +1856,7 @@
         auto &field = **it;
         if (field.deprecated) continue;
         auto camel_name = Name(field);
+        if (camel_name == struct_def.name) { camel_name += "_"; }
         switch (field.value.type.base_type) {
           case BASE_TYPE_STRUCT: {
             if (struct_def.fixed) {
@@ -2094,6 +2111,7 @@
       auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
       if (field.IsScalarOptional()) type_name += "?";
       auto camel_name = Name(field);
+      if (camel_name == struct_def.name) { camel_name += "_"; }
       if (opts.cs_gen_json_serializer) {
         if (IsUnion(field.value.type)) {
           auto utype_name = NamespacedName(*field.value.type.enum_def);
@@ -2159,7 +2177,9 @@
       if (field.deprecated) continue;
       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
-      code += "    this." + Name(field) + " = ";
+      auto camel_name = Name(field);
+      if (camel_name == struct_def.name) { camel_name += "_"; }
+      code += "    this." + camel_name + " = ";
       auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
       if (IsScalar(field.value.type.base_type)) {
         code += GenDefaultValue(field) + ";\n";
diff --git a/src/idl_gen_dart.cpp b/src/idl_gen_dart.cpp
index ff2c1a5..0bf230d 100644
--- a/src/idl_gen_dart.cpp
+++ b/src/idl_gen_dart.cpp
@@ -21,26 +21,57 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
+#include "idl_namer.h"
 
 namespace flatbuffers {
 
 namespace dart {
 
+namespace {
+
+static Namer::Config DartDefaultConfig() {
+  return { /*types=*/Case::kUpperCamel,
+           /*constants=*/Case::kScreamingSnake,
+           /*methods=*/Case::kLowerCamel,
+           /*functions=*/Case::kUnknown,  // unused.
+           /*fields=*/Case::kLowerCamel,
+           /*variables=*/Case::kLowerCamel,
+           /*variants=*/Case::kKeep,
+           /*enum_variant_seperator=*/".",
+           /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
+           /*namespaces=*/Case::kSnake2,
+           /*namespace_seperator=*/".",
+           /*object_prefix=*/"",
+           /*object_suffix=*/"T",
+           /*keyword_prefix=*/"$",
+           /*keyword_suffix=*/"",
+           /*filenames=*/Case::kKeep,
+           /*directories=*/Case::kKeep,
+           /*output_path=*/"",
+           /*filename_suffix=*/"_generated",
+           /*filename_extension=*/".dart" };
+}
+
+static std::set<std::string> DartKeywords() {
+  // see https://www.dartlang.org/guides/language/language-tour#keywords
+  // yield*, async*, and sync* shouldn't be proble
+  return {
+    "abstract", "else",       "import",    "show",     "as",        "enum",
+    "in",       "static",     "assert",    "export",   "interface", "super",
+    "async",    "extends",    "is",        "switch",   "await",     "extension",
+    "late",     "sync",       "break",     "external", "library",   "this",
+    "case",     "factory",    "mixin",     "throw",    "catch",     "false",
+    "new",      "true",       "class",     "final",    "null",      "try",
+    "const",    "finally",    "on",        "typedef",  "continue",  "for",
+    "operator", "var",        "covariant", "Function", "part",      "void",
+    "default",  "get",        "required",  "while",    "deferred",  "hide",
+    "rethrow",  "with",       "do",        "if",       "return",    "yield",
+    "dynamic",  "implements", "set",
+  };
+}
+} // namespace
+
 const std::string _kFb = "fb";
-// see https://www.dartlang.org/guides/language/language-tour#keywords
-// yeild*, async*, and sync* shouldn't be problems anyway but keeping them in
-static const char *keywords[] = {
-  "abstract",   "deferred", "if",       "super",   "as",       "do",
-  "implements", "switch",   "assert",   "dynamic", "import",   "sync*",
-  "async",      "else",     "in",       "this",    "async*",   "enum",
-  "is",         "throw",    "await",    "export",  "library",  "true",
-  "break",      "external", "new",      "try",     "case",     "extends",
-  "null",       "typedef",  "catch",    "factory", "operator", "var",
-  "class",      "false",    "part",     "void",    "const",    "final",
-  "rethrow",    "while",    "continue", "finally", "return",   "with",
-  "covariant",  "for",      "set",      "yield",   "default",  "get",
-  "static",     "yield*"
-};
 
 // Iterate through all definitions we haven't generate code for (enums, structs,
 // and tables) and output them to a single file.
@@ -50,14 +81,16 @@
 
   DartGenerator(const Parser &parser, const std::string &path,
                 const std::string &file_name)
-      : BaseGenerator(parser, path, file_name, "", ".", "dart") {}
+      : BaseGenerator(parser, path, file_name, "", ".", "dart"),
+        namer_(WithFlagOptions(DartDefaultConfig(), parser.opts, path),
+               DartKeywords()) {}
   // Iterate through all definitions we haven't generate code for (enums,
   // structs, and tables) and output them to a single file.
   bool generate() {
     std::string code;
     namespace_code_map namespace_code;
-    GenerateEnums(&namespace_code);
-    GenerateStructs(&namespace_code);
+    GenerateEnums(namespace_code);
+    GenerateStructs(namespace_code);
 
     for (auto kv = namespace_code.begin(); kv != namespace_code.end(); ++kv) {
       code.clear();
@@ -75,31 +108,23 @@
       for (auto kv2 = namespace_code.begin(); kv2 != namespace_code.end();
            ++kv2) {
         if (kv2->first != kv->first) {
-          code +=
-              "import '" +
-              GeneratedFileName(
-                  "./",
-                  file_name_ + (!kv2->first.empty() ? "_" + kv2->first : ""),
-                  parser_.opts) +
-              "' as " + ImportAliasName(kv2->first) + ";\n";
+          code += "import './" + Filename(kv2->first, /*path=*/false) +
+                  "' as " + ImportAliasName(kv2->first) + ";\n";
         }
       }
       code += "\n";
       code += kv->second;
 
-      if (!SaveFile(
-              GeneratedFileName(
-                  path_,
-                  file_name_ + (!kv->first.empty() ? "_" + kv->first : ""),
-                  parser_.opts)
-                  .c_str(),
-              code, false)) {
-        return false;
-      }
+      if (!SaveFile(Filename(kv->first).c_str(), code, false)) { return false; }
     }
     return true;
   }
 
+  std::string Filename(const std::string &suffix, bool path = true) const {
+    return (path ? path_ : "") +
+           namer_.File(file_name_ + (suffix.empty() ? "" : "_" + suffix));
+  }
+
  private:
   static std::string ImportAliasName(const std::string &ns) {
     std::string ret;
@@ -113,64 +138,15 @@
     return ret;
   }
 
-  static std::string BuildNamespaceName(const Namespace &ns) {
-    if (ns.components.empty()) { return ""; }
-    std::stringstream sstream;
-    std::copy(ns.components.begin(), ns.components.end() - 1,
-              std::ostream_iterator<std::string>(sstream, "."));
-
-    auto ret = sstream.str() + ns.components.back();
-    for (size_t i = 0; i < ret.size(); i++) {
-      auto lower = CharToLower(ret[i]);
-      if (lower != ret[i]) {
-        ret[i] = lower;
-        if (i != 0 && ret[i - 1] != '.') {
-          ret.insert(i, "_");
-          i++;
-        }
-      }
-    }
-    // std::transform(ret.begin(), ret.end(), ret.begin(), CharToLower);
-    return ret;
-  }
-
-  void GenIncludeDependencies(std::string *code,
-                              const std::string &the_namespace) {
-    for (auto it = parser_.included_files_.begin();
-         it != parser_.included_files_.end(); ++it) {
-      if (it->second.empty()) continue;
-
-      auto noext = flatbuffers::StripExtension(it->second);
-      auto basename = flatbuffers::StripPath(noext);
-
-      *code +=
-          "import '" +
-          GeneratedFileName(
-              "", basename + (the_namespace == "" ? "" : "_" + the_namespace),
-              parser_.opts) +
-          "';\n";
-    }
-  }
-
-  static std::string EscapeKeyword(const std::string &name) {
-    for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
-      if (name == keywords[i]) {
-        return ConvertCase(name + "_", Case::kLowerCamel);
-      }
-    }
-
-    return ConvertCase(name, Case::kLowerCamel);
-  }
-
-  void GenerateEnums(namespace_code_map *namespace_code) {
+  void GenerateEnums(namespace_code_map &namespace_code) {
     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
          ++it) {
       auto &enum_def = **it;
-      GenEnum(enum_def, namespace_code);  // enum_code_ptr);
+      GenEnum(enum_def, namespace_code);
     }
   }
 
-  void GenerateStructs(namespace_code_map *namespace_code) {
+  void GenerateStructs(namespace_code_map &namespace_code) {
     for (auto it = parser_.structs_.vec.begin();
          it != parser_.structs_.vec.end(); ++it) {
       auto &struct_def = **it;
@@ -180,80 +156,50 @@
 
   // Generate a documentation comment, if available.
   static void GenDocComment(const std::vector<std::string> &dc,
-                            std::string *code_ptr,
-                            const std::string &extra_lines,
-                            const char *indent = nullptr) {
-    if (dc.empty() && extra_lines.empty()) {
-      // Don't output empty comment blocks with 0 lines of comment content.
-      return;
-    }
-
-    auto &code = *code_ptr;
-
+                            const char *indent, std::string &code) {
     for (auto it = dc.begin(); it != dc.end(); ++it) {
       if (indent) code += indent;
       code += "/// " + *it + "\n";
     }
-    if (!extra_lines.empty()) {
-      if (!dc.empty()) {
-        if (indent) code += indent;
-        code += "///\n";
-      }
-      if (indent) code += indent;
-      std::string::size_type start = 0;
-      for (;;) {
-        auto end = extra_lines.find('\n', start);
-        if (end != std::string::npos) {
-          code += "/// " + extra_lines.substr(start, end - start) + "\n";
-          start = end + 1;
-        } else {
-          code += "/// " + extra_lines.substr(start) + "\n";
-          break;
-        }
-      }
-    }
-  }
-
-  static void GenDocComment(std::string *code_ptr,
-                            const std::string &extra_lines) {
-    GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
   }
 
   // Generate an enum declaration and an enum string lookup table.
-  void GenEnum(EnumDef &enum_def, namespace_code_map *namespace_code) {
+  void GenEnum(EnumDef &enum_def, namespace_code_map &namespace_code) {
     if (enum_def.generated) return;
-    auto ns = BuildNamespaceName(*enum_def.defined_namespace);
-    std::string code;
-    GenDocComment(enum_def.doc_comment, &code, "");
+    std::string &code =
+        namespace_code[namer_.Namespace(*enum_def.defined_namespace)];
+    GenDocComment(enum_def.doc_comment, "", code);
 
-    auto name = enum_def.is_union ? enum_def.name + "TypeId" : enum_def.name;
+    const std::string enum_type =
+        namer_.Type(enum_def) + (enum_def.is_union ? "TypeId" : "");
     const bool is_bit_flags =
         enum_def.attributes.Lookup("bit_flags") != nullptr;
     // The flatbuffer schema language allows bit flag enums to potentially have
     // a default value of zero, even if it's not a valid enum value...
     const bool permit_zero = is_bit_flags;
 
-    code += "class " + name + " {\n";
+    code += "class " + enum_type + " {\n";
     code += "  final int value;\n";
-    code += "  const " + name + "._(this.value);\n\n";
-    code += "  factory " + name + ".fromValue(int value) {\n";
+    code += "  const " + enum_type + "._(this.value);\n\n";
+    code += "  factory " + enum_type + ".fromValue(int value) {\n";
     code += "    final result = values[value];\n";
     code += "    if (result == null) {\n";
     if (permit_zero) {
       code += "      if (value == 0) {\n";
-      code += "        return " + name + "._(0);\n";
+      code += "        return " + enum_type + "._(0);\n";
       code += "      } else {\n";
     }
     code += "        throw StateError('Invalid value $value for bit flag enum ";
-    code += name + "');\n";
+    code += enum_type + "');\n";
     if (permit_zero) { code += "      }\n"; }
     code += "    }\n";
 
     code += "    return result;\n";
     code += "  }\n\n";
 
-    code += "  static " + name + "? _createOrNull(int? value) => \n";
-    code += "      value == null ? null : " + name + ".fromValue(value);\n\n";
+    code += "  static " + enum_type + "? _createOrNull(int? value) => \n";
+    code +=
+        "      value == null ? null : " + enum_type + ".fromValue(value);\n\n";
 
     // this is meaningless for bit_flags
     // however, note that unlike "regular" dart enums this enum can still have
@@ -271,53 +217,52 @@
 
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       auto &ev = **it;
+      const auto enum_var = namer_.Variant(ev);
 
       if (!ev.doc_comment.empty()) {
         if (it != enum_def.Vals().begin()) { code += '\n'; }
-        GenDocComment(ev.doc_comment, &code, "", "  ");
+        GenDocComment(ev.doc_comment, "  ", code);
       }
-      code += "  static const " + name + " " + ev.name + " = " + name + "._(" +
-              enum_def.ToString(ev) + ");\n";
+      code += "  static const " + enum_type + " " + enum_var + " = " +
+              enum_type + "._(" + enum_def.ToString(ev) + ");\n";
     }
 
-    code += "  static const Map<int, " + name + "> values = {\n";
+    code += "  static const Map<int, " + enum_type + "> values = {\n";
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       auto &ev = **it;
+      const auto enum_var = namer_.Variant(ev);
       if (it != enum_def.Vals().begin()) code += ",\n";
-      code += "    " + enum_def.ToString(ev) + ": " + ev.name;
+      code += "    " + enum_def.ToString(ev) + ": " + enum_var;
     }
     code += "};\n\n";
 
-    code += "  static const " + _kFb + ".Reader<" + name + "> reader = _" +
-            name + "Reader();\n\n";
+    code += "  static const " + _kFb + ".Reader<" + enum_type + "> reader = _" +
+            enum_type + "Reader();\n\n";
     code += "  @override\n";
     code += "  String toString() {\n";
-    code += "    return '" + name + "{value: $value}';\n";
+    code += "    return '" + enum_type + "{value: $value}';\n";
     code += "  }\n";
     code += "}\n\n";
 
-    GenEnumReader(enum_def, name, &code);
-    (*namespace_code)[ns] += code;
+    GenEnumReader(enum_def, enum_type, code);
   }
 
-  void GenEnumReader(EnumDef &enum_def, const std::string &name,
-                     std::string *code_ptr) {
-    auto &code = *code_ptr;
-
-    code += "class _" + name + "Reader extends " + _kFb + ".Reader<" + name +
-            "> {\n";
-    code += "  const _" + name + "Reader();\n\n";
+  void GenEnumReader(EnumDef &enum_def, const std::string &enum_type,
+                     std::string &code) {
+    code += "class _" + enum_type + "Reader extends " + _kFb + ".Reader<" +
+            enum_type + "> {\n";
+    code += "  const _" + enum_type + "Reader();\n\n";
     code += "  @override\n";
-    code += "  int get size => 1;\n\n";
+    code += "  int get size => " + EnumSize(enum_def.underlying_type) + ";\n\n";
     code += "  @override\n";
-    code +=
-        "  " + name + " read(" + _kFb + ".BufferContext bc, int offset) =>\n";
-    code += "      " + name + ".fromValue(const " + _kFb + "." +
+    code += "  " + enum_type + " read(" + _kFb +
+            ".BufferContext bc, int offset) =>\n";
+    code += "      " + enum_type + ".fromValue(const " + _kFb + "." +
             GenType(enum_def.underlying_type) + "Reader().read(bc, offset));\n";
     code += "}\n\n";
   }
 
-  static std::string GenType(const Type &type) {
+  std::string GenType(const Type &type) {
     switch (type.base_type) {
       case BASE_TYPE_BOOL: return "Bool";
       case BASE_TYPE_CHAR: return "Int8";
@@ -333,12 +278,30 @@
       case BASE_TYPE_DOUBLE: return "Float64";
       case BASE_TYPE_STRING: return "String";
       case BASE_TYPE_VECTOR: return GenType(type.VectorType());
-      case BASE_TYPE_STRUCT: return type.struct_def->name;
-      case BASE_TYPE_UNION: return type.enum_def->name + "TypeId";
+      case BASE_TYPE_STRUCT: return namer_.Type(*type.struct_def);
+      case BASE_TYPE_UNION: return namer_.Type(*type.enum_def) + "TypeId";
       default: return "Table";
     }
   }
 
+  static std::string EnumSize(const Type &type) {
+    switch (type.base_type) {
+      case BASE_TYPE_BOOL:
+      case BASE_TYPE_CHAR:
+      case BASE_TYPE_UTYPE:
+      case BASE_TYPE_UCHAR: return "1";
+      case BASE_TYPE_SHORT:
+      case BASE_TYPE_USHORT: return "2";
+      case BASE_TYPE_INT:
+      case BASE_TYPE_UINT:
+      case BASE_TYPE_FLOAT: return "4";
+      case BASE_TYPE_LONG:
+      case BASE_TYPE_ULONG:
+      case BASE_TYPE_DOUBLE: return "8";
+      default: return "1";
+    }
+  }
+
   std::string GenReaderTypeName(const Type &type, Namespace *current_namespace,
                                 const FieldDef &def,
                                 bool parent_is_vector = false, bool lazy = true,
@@ -378,11 +341,11 @@
                               std::string struct_type_suffix = "") {
     if (type.enum_def) {
       if (type.enum_def->is_union && type.base_type != BASE_TYPE_UNION) {
-        return type.enum_def->name + "TypeId";
+        return namer_.Type(*type.enum_def) + "TypeId";
       } else if (type.enum_def->is_union) {
         return "dynamic";
       } else if (type.base_type != BASE_TYPE_VECTOR) {
-        return type.enum_def->name;
+        return namer_.Type(*type.enum_def);
       }
     }
 
@@ -400,8 +363,9 @@
       case BASE_TYPE_DOUBLE: return "double";
       case BASE_TYPE_STRING: return "String";
       case BASE_TYPE_STRUCT:
-        return MaybeWrapNamespace(type.struct_def->name + struct_type_suffix,
-                                  current_namespace, def);
+        return MaybeWrapNamespace(
+            namer_.Type(*type.struct_def) + struct_type_suffix,
+            current_namespace, def);
       case BASE_TYPE_VECTOR:
         return "List<" +
                GenDartTypeName(type.VectorType(), current_namespace, def,
@@ -420,21 +384,19 @@
     return typeName;
   }
 
-  static const std::string MaybeWrapNamespace(const std::string &type_name,
-                                              Namespace *current_ns,
-                                              const FieldDef &field) {
-    auto curr_ns_str = BuildNamespaceName(*current_ns);
-    std::string field_ns_str = "";
-    if (field.value.type.struct_def) {
-      field_ns_str +=
-          BuildNamespaceName(*field.value.type.struct_def->defined_namespace);
-    } else if (field.value.type.enum_def) {
-      field_ns_str +=
-          BuildNamespaceName(*field.value.type.enum_def->defined_namespace);
-    }
+  std::string MaybeWrapNamespace(const std::string &type_name,
+                                 Namespace *current_ns,
+                                 const FieldDef &field) const {
+    const std::string current_namespace = namer_.Namespace(*current_ns);
+    const std::string field_namespace =
+        field.value.type.struct_def
+            ? namer_.Namespace(*field.value.type.struct_def->defined_namespace)
+        : field.value.type.enum_def
+            ? namer_.Namespace(*field.value.type.enum_def->defined_namespace)
+            : "";
 
-    if (field_ns_str != "" && field_ns_str != curr_ns_str) {
-      return ImportAliasName(field_ns_str) + "." + type_name;
+    if (field_namespace != "" && field_namespace != current_namespace) {
+      return ImportAliasName(field_namespace) + "." + type_name;
     } else {
       return type_name;
     }
@@ -442,29 +404,29 @@
 
   // Generate an accessor struct with constructor for a flatbuffers struct.
   void GenStruct(const StructDef &struct_def,
-                 namespace_code_map *namespace_code) {
+                 namespace_code_map &namespace_code) {
     if (struct_def.generated) return;
 
-    auto object_namespace = BuildNamespaceName(*struct_def.defined_namespace);
-    std::string code;
+    std::string &code =
+        namespace_code[namer_.Namespace(*struct_def.defined_namespace)];
 
-    const auto &object_name = struct_def.name;
+    const auto &struct_type = namer_.Type(struct_def);
 
     // Emit constructor
 
-    GenDocComment(struct_def.doc_comment, &code, "");
+    GenDocComment(struct_def.doc_comment, "", code);
 
-    auto reader_name = "_" + object_name + "Reader";
-    auto builder_name = object_name + "Builder";
-    auto object_builder_name = object_name + "ObjectBuilder";
+    auto reader_name = "_" + struct_type + "Reader";
+    auto builder_name = struct_type + "Builder";
+    auto object_builder_name = struct_type + "ObjectBuilder";
 
     std::string reader_code, builder_code;
 
-    code += "class " + object_name + " {\n";
+    code += "class " + struct_type + " {\n";
 
-    code += "  " + object_name + "._(this._bc, this._bcOffset);\n";
+    code += "  " + struct_type + "._(this._bc, this._bcOffset);\n";
     if (!struct_def.fixed) {
-      code += "  factory " + object_name + "(List<int> bytes) {\n";
+      code += "  factory " + struct_type + "(List<int> bytes) {\n";
       code +=
           "    final rootRef = " + _kFb + ".BufferContext.fromBytes(bytes);\n";
       code += "    return reader.read(rootRef, 0);\n";
@@ -472,7 +434,7 @@
     }
 
     code += "\n";
-    code += "  static const " + _kFb + ".Reader<" + object_name +
+    code += "  static const " + _kFb + ".Reader<" + struct_type +
             "> reader = " + reader_name + "();\n\n";
 
     code += "  final " + _kFb + ".BufferContext _bc;\n";
@@ -481,20 +443,20 @@
     std::vector<std::pair<int, FieldDef *>> non_deprecated_fields;
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      FieldDef &field = **it;
       if (field.deprecated) continue;
       auto offset = static_cast<int>(it - struct_def.fields.vec.begin());
       non_deprecated_fields.push_back(std::make_pair(offset, &field));
     }
 
-    GenImplementationGetters(struct_def, non_deprecated_fields, &code);
+    GenImplementationGetters(struct_def, non_deprecated_fields, code);
 
     if (parser_.opts.generate_object_based_api) {
       code +=
           "\n" + GenStructObjectAPIUnpack(struct_def, non_deprecated_fields);
 
-      code += "\n  static int pack(fb.Builder fbBuilder, " + struct_def.name +
-              "T? object) {\n";
+      code += "\n  static int pack(fb.Builder fbBuilder, " +
+              namer_.ObjectType(struct_def) + "? object) {\n";
       code += "    if (object == null) return 0;\n";
       code += "    return object.pack(fbBuilder);\n";
       code += "  }\n";
@@ -506,15 +468,13 @@
       code += GenStructObjectAPI(struct_def, non_deprecated_fields);
     }
 
-    GenReader(struct_def, &reader_name, &reader_code);
-    GenBuilder(struct_def, non_deprecated_fields, &builder_name, &builder_code);
-    GenObjectBuilder(struct_def, non_deprecated_fields, &object_builder_name,
-                     &builder_code);
+    GenReader(struct_def, reader_name, reader_code);
+    GenBuilder(struct_def, non_deprecated_fields, builder_name, builder_code);
+    GenObjectBuilder(struct_def, non_deprecated_fields, object_builder_name,
+                     builder_code);
 
     code += reader_code;
     code += builder_code;
-
-    (*namespace_code)[object_namespace] += code;
   }
 
   // Generate an accessor struct with constructor for a flatbuffers struct.
@@ -522,23 +482,23 @@
       const StructDef &struct_def,
       const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields) {
     std::string code;
-    GenDocComment(struct_def.doc_comment, &code, "");
+    GenDocComment(struct_def.doc_comment, "", code);
 
-    std::string class_name = struct_def.name + "T";
-    code += "class " + class_name + " implements " + _kFb + ".Packable {\n";
+    std::string object_type = namer_.ObjectType(struct_def);
+    code += "class " + object_type + " implements " + _kFb + ".Packable {\n";
 
     std::string constructor_args;
     for (auto it = non_deprecated_fields.begin();
          it != non_deprecated_fields.end(); ++it) {
       const FieldDef &field = *it->second;
 
-      std::string field_name = ConvertCase(field.name, Case::kLowerCamel);
-      std::string defaultValue = getDefaultValue(field.value);
-      std::string type_name =
+      const std::string field_name = namer_.Field(field);
+      const std::string defaultValue = getDefaultValue(field.value);
+      const std::string type_name =
           GenDartTypeName(field.value.type, struct_def.defined_namespace, field,
                           defaultValue.empty() && !struct_def.fixed, "T");
 
-      GenDocComment(field.doc_comment, &code, "", "  ");
+      GenDocComment(field.doc_comment, "  ", code);
       code += "  " + type_name + " " + field_name + ";\n";
 
       if (!constructor_args.empty()) constructor_args += ",\n";
@@ -549,10 +509,10 @@
         if (IsEnum(field.value.type)) {
           auto &enum_def = *field.value.type.enum_def;
           if (auto val = enum_def.FindByValue(defaultValue)) {
-            constructor_args += " = " + enum_def.name + "." + val->name;
+            constructor_args += " = " + namer_.EnumVariant(enum_def, *val);
           } else {
-            constructor_args +=
-                " = const " + enum_def.name + "._(" + defaultValue + ")";
+            constructor_args += " = const " + namer_.Type(enum_def) + "._(" +
+                                defaultValue + ")";
           }
         } else {
           constructor_args += " = " + defaultValue;
@@ -561,12 +521,12 @@
     }
 
     if (!constructor_args.empty()) {
-      code += "\n  " + class_name + "({\n" + constructor_args + "});\n\n";
+      code += "\n  " + object_type + "({\n" + constructor_args + "});\n\n";
     }
 
     code += GenStructObjectAPIPack(struct_def, non_deprecated_fields);
     code += "\n";
-    code += GenToString(class_name, non_deprecated_fields);
+    code += GenToString(object_type, non_deprecated_fields);
 
     code += "}\n\n";
     return code;
@@ -581,7 +541,7 @@
          it != non_deprecated_fields.end(); ++it) {
       const FieldDef &field = *it->second;
 
-      std::string field_name = ConvertCase(field.name, Case::kLowerCamel);
+      const std::string field_name = namer_.Field(field);
       if (!constructor_args.empty()) constructor_args += ",\n";
       constructor_args += "      " + field_name + ": ";
 
@@ -612,8 +572,8 @@
       }
     }
 
-    std::string class_name = struct_def.name + "T";
-    std::string code = "  " + class_name + " unpack() => " + class_name + "(";
+    const std::string object_type = namer_.ObjectType(struct_def);
+    std::string code = "  " + object_type + " unpack() => " + object_type + "(";
     if (!constructor_args.empty()) code += "\n" + constructor_args;
     code += ");\n";
     return code;
@@ -640,7 +600,7 @@
     if (std::equal(root_namespace->components.begin(),
                    root_namespace->components.end(),
                    qualified_name_parts.begin())) {
-      return type.struct_def->name;
+      return namer_.Type(*type.struct_def);
     }
 
     std::string ns;
@@ -660,39 +620,35 @@
       if (it != qualified_name_parts.end() - 1) { ns += "_"; }
     }
 
-    return ns + "." + type.struct_def->name;
+    return ns + "." + namer_.Type(*type.struct_def);
   }
 
   void GenImplementationGetters(
       const StructDef &struct_def,
-      std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
-      std::string *code_ptr) {
-    auto &code = *code_ptr;
-
+      const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
+      std::string &code) {
     for (auto it = non_deprecated_fields.begin();
          it != non_deprecated_fields.end(); ++it) {
-      auto pair = *it;
-      auto &field = *pair.second;
+      const FieldDef &field = *it->second;
 
-      std::string field_name = ConvertCase(field.name, Case::kLowerCamel);
-      std::string defaultValue = getDefaultValue(field.value);
-      bool isNullable = defaultValue.empty() && !struct_def.fixed;
-      std::string type_name =
+      const std::string field_name = namer_.Field(field);
+      const std::string defaultValue = getDefaultValue(field.value);
+      const bool isNullable = defaultValue.empty() && !struct_def.fixed;
+      const std::string type_name =
           GenDartTypeName(field.value.type, struct_def.defined_namespace, field,
                           isNullable, "");
 
-      GenDocComment(field.doc_comment, &code, "", "  ");
+      GenDocComment(field.doc_comment, "  ", code);
 
       code += "  " + type_name + " get " + field_name;
       if (field.value.type.base_type == BASE_TYPE_UNION) {
         code += " {\n";
         code += "    switch (" + field_name + "Type?.value) {\n";
-        auto &enum_def = *field.value.type.enum_def;
+        const auto &enum_def = *field.value.type.enum_def;
         for (auto en_it = enum_def.Vals().begin() + 1;
              en_it != enum_def.Vals().end(); ++en_it) {
-          auto &ev = **en_it;
-
-          auto enum_name = NamespaceAliasFromUnionType(
+          const auto &ev = **en_it;
+          const auto enum_name = NamespaceAliasFromUnionType(
               enum_def.defined_namespace, ev.union_type);
           code += "      case " + enum_def.ToString(ev) + ": return " +
                   enum_name + ".reader.vTableGetNullable(_bc, _bcOffset, " +
@@ -733,7 +689,7 @@
     }
 
     code += "\n";
-    code += GenToString(struct_def.name, non_deprecated_fields);
+    code += GenToString(namer_.Type(struct_def), non_deprecated_fields);
   }
 
   std::string GenToString(
@@ -745,10 +701,15 @@
     code += "    return '" + object_name + "{";
     for (auto it = non_deprecated_fields.begin();
          it != non_deprecated_fields.end(); ++it) {
-      auto pair = *it;
-      auto &field = *pair.second;
-      code += ConvertCase(field.name, Case::kLowerCamel) + ": $" +
-              ConvertCase(field.name, Case::kLowerCamel);
+      const std::string field = namer_.Field(*it->second);
+      // We need to escape the fact that some fields have $ in the name which is
+      // also used in symbol/string substitution.
+      std::string escaped_field;
+      for (size_t i = 0; i < field.size(); i++) {
+        if (field[i] == '$') escaped_field.push_back('\\');
+        escaped_field.push_back(field[i]);
+      }
+      code += escaped_field + ": ${" + field + "}";
       if (it != non_deprecated_fields.end() - 1) { code += ", "; }
     }
     code += "}';\n";
@@ -779,11 +740,9 @@
     }
   }
 
-  void GenReader(const StructDef &struct_def, std::string *reader_name_ptr,
-                 std::string *code_ptr) {
-    auto &code = *code_ptr;
-    auto &reader_name = *reader_name_ptr;
-    auto &impl_name = struct_def.name;
+  void GenReader(const StructDef &struct_def, const std::string &reader_name,
+                 std::string &code) {
+    const auto struct_type = namer_.Type(struct_def);
 
     code += "class " + reader_name + " extends " + _kFb;
     if (struct_def.fixed) {
@@ -791,7 +750,7 @@
     } else {
       code += ".TableReader<";
     }
-    code += impl_name + "> {\n";
+    code += struct_type + "> {\n";
     code += "  const " + reader_name + "();\n\n";
 
     if (struct_def.fixed) {
@@ -799,27 +758,26 @@
       code += "  int get size => " + NumToString(struct_def.bytesize) + ";\n\n";
     }
     code += "  @override\n";
-    code += "  " + impl_name +
+    code += "  " + struct_type +
             " createObject(fb.BufferContext bc, int offset) => \n    " +
-            impl_name + "._(bc, offset);\n";
+            struct_type + "._(bc, offset);\n";
     code += "}\n\n";
   }
 
-  void GenBuilder(const StructDef &struct_def,
-                  std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
-                  std::string *builder_name_ptr, std::string *code_ptr) {
+  void GenBuilder(
+      const StructDef &struct_def,
+      const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
+      const std::string &builder_name, std::string &code) {
     if (non_deprecated_fields.size() == 0) { return; }
-    auto &code = *code_ptr;
-    auto &builder_name = *builder_name_ptr;
 
     code += "class " + builder_name + " {\n";
     code += "  " + builder_name + "(this.fbBuilder);\n\n";
     code += "  final " + _kFb + ".Builder fbBuilder;\n\n";
 
     if (struct_def.fixed) {
-      StructBuilderBody(struct_def, non_deprecated_fields, code_ptr);
+      StructBuilderBody(struct_def, non_deprecated_fields, code);
     } else {
-      TableBuilderBody(struct_def, non_deprecated_fields, code_ptr);
+      TableBuilderBody(struct_def, non_deprecated_fields, code);
     }
 
     code += "}\n\n";
@@ -827,15 +785,13 @@
 
   void StructBuilderBody(
       const StructDef &struct_def,
-      std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
-      std::string *code_ptr) {
-    auto &code = *code_ptr;
-
+      const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
+      std::string &code) {
     code += "  int finish(";
     for (auto it = non_deprecated_fields.begin();
          it != non_deprecated_fields.end(); ++it) {
-      auto pair = *it;
-      auto &field = *pair.second;
+      const FieldDef &field = *it->second;
+      const std::string field_name = namer_.Field(field);
 
       if (IsStruct(field.value.type)) {
         code += "fb.StructBuilder";
@@ -843,25 +799,25 @@
         code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
                                 field);
       }
-      code += " " + field.name;
+      code += " " + field_name;
       if (it != non_deprecated_fields.end() - 1) { code += ", "; }
     }
     code += ") {\n";
 
     for (auto it = non_deprecated_fields.rbegin();
          it != non_deprecated_fields.rend(); ++it) {
-      auto pair = *it;
-      auto &field = *pair.second;
+      const FieldDef &field = *it->second;
+      const std::string field_name = namer_.Field(field);
 
       if (field.padding) {
         code += "    fbBuilder.pad(" + NumToString(field.padding) + ");\n";
       }
 
       if (IsStruct(field.value.type)) {
-        code += "    " + field.name + "();\n";
+        code += "    " + field_name + "();\n";
       } else {
         code += "    fbBuilder.put" + GenType(field.value.type) + "(";
-        code += field.name;
+        code += field_name;
         if (field.value.type.enum_def) { code += ".value"; }
         code += ");\n";
       }
@@ -872,10 +828,8 @@
 
   void TableBuilderBody(
       const StructDef &struct_def,
-      std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
-      std::string *code_ptr) {
-    auto &code = *code_ptr;
-
+      const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
+      std::string &code) {
     code += "  void begin() {\n";
     code += "    fbBuilder.startTable(" +
             NumToString(struct_def.fields.vec.size()) + ");\n";
@@ -883,28 +837,27 @@
 
     for (auto it = non_deprecated_fields.begin();
          it != non_deprecated_fields.end(); ++it) {
-      auto pair = *it;
-      auto &field = *pair.second;
-      auto offset = pair.first;
+      const auto &field = *it->second;
+      const auto offset = it->first;
+      const std::string add_field = namer_.Method("add", field);
+      const std::string field_var = namer_.Variable(field);
 
       if (IsScalar(field.value.type.base_type)) {
-        code += "  int add" + ConvertCase(field.name, Case::kUpperCamel) + "(";
+        code += "  int " + add_field + "(";
         code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
                                 field);
-        code += "? " + ConvertCase(field.name, Case::kLowerCamel) + ") {\n";
+        code += "? " + field_var + ") {\n";
         code += "    fbBuilder.add" + GenType(field.value.type) + "(" +
                 NumToString(offset) + ", ";
-        code += ConvertCase(field.name, Case::kLowerCamel);
+        code += field_var;
         if (field.value.type.enum_def) { code += "?.value"; }
         code += ");\n";
       } else if (IsStruct(field.value.type)) {
-        code += "  int add" + ConvertCase(field.name, Case::kUpperCamel) +
-                "(int offset) {\n";
+        code += "  int " + add_field + "(int offset) {\n";
         code +=
             "    fbBuilder.addStruct(" + NumToString(offset) + ", offset);\n";
       } else {
-        code += "  int add" + ConvertCase(field.name, Case::kUpperCamel) +
-                "Offset(int? offset) {\n";
+        code += "  int " + add_field + "Offset(int? offset) {\n";
         code +=
             "    fbBuilder.addOffset(" + NumToString(offset) + ", offset);\n";
       }
@@ -920,21 +873,17 @@
 
   void GenObjectBuilder(
       const StructDef &struct_def,
-      std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
-      std::string *builder_name_ptr, std::string *code_ptr) {
-    auto &code = *code_ptr;
-    auto &builder_name = *builder_name_ptr;
-
+      const std::vector<std::pair<int, FieldDef *>> &non_deprecated_fields,
+      const std::string &builder_name, std::string &code) {
     code += "class " + builder_name + " extends " + _kFb + ".ObjectBuilder {\n";
     for (auto it = non_deprecated_fields.begin();
          it != non_deprecated_fields.end(); ++it) {
-      auto pair = *it;
-      auto &field = *pair.second;
+      const FieldDef &field = *it->second;
 
       code += "  final " +
               GenDartTypeName(field.value.type, struct_def.defined_namespace,
                               field, !struct_def.fixed, "ObjectBuilder") +
-              " _" + ConvertCase(field.name, Case::kLowerCamel) + ";\n";
+              " _" + namer_.Variable(field) + ";\n";
     }
     code += "\n";
     code += "  " + builder_name + "(";
@@ -943,24 +892,21 @@
       code += "{\n";
       for (auto it = non_deprecated_fields.begin();
            it != non_deprecated_fields.end(); ++it) {
-        auto pair = *it;
-        auto &field = *pair.second;
+        const FieldDef &field = *it->second;
 
         code += "    ";
         code += (struct_def.fixed ? "required " : "") +
                 GenDartTypeName(field.value.type, struct_def.defined_namespace,
                                 field, !struct_def.fixed, "ObjectBuilder") +
-                " " + ConvertCase(field.name, Case::kLowerCamel) + ",\n";
+                " " + namer_.Variable(field) + ",\n";
       }
       code += "  })\n";
       code += "      : ";
       for (auto it = non_deprecated_fields.begin();
            it != non_deprecated_fields.end(); ++it) {
-        auto pair = *it;
-        auto &field = *pair.second;
+        const FieldDef &field = *it->second;
 
-        code += "_" + ConvertCase(field.name, Case::kLowerCamel) + " = " +
-                ConvertCase(field.name, Case::kLowerCamel);
+        code += "_" + namer_.Variable(field) + " = " + namer_.Variable(field);
         if (it == non_deprecated_fields.end() - 1) {
           code += ";\n\n";
         } else {
@@ -1000,11 +946,9 @@
       if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type))
         continue;
 
-      std::string offset_name =
-          ConvertCase(field.name, Case::kLowerCamel) + "Offset";
-      std::string field_name = (prependUnderscore ? "_" : "") +
-                               ConvertCase(field.name, Case::kLowerCamel);
-
+      std::string offset_name = namer_.Variable(field) + "Offset";
+      std::string field_name =
+          (prependUnderscore ? "_" : "") + namer_.Variable(field);
       // custom handling for fixed-sized struct in pack()
       if (pack && IsVector(field.value.type) &&
           field.value.type.VectorType().base_type == BASE_TYPE_STRUCT &&
@@ -1013,7 +957,7 @@
         code += "    if (" + field_name + " != null) {\n";
         code +=
             "      for (var e in " + field_name + "!) { e.pack(fbBuilder); }\n";
-        code += "      " + ConvertCase(field.name, Case::kLowerCamel) +
+        code += "      " + namer_.Variable(field) +
                 "Offset = fbBuilder.endStructVector(" + field_name +
                 "!.length);\n";
         code += "    }\n";
@@ -1072,8 +1016,8 @@
 
     for (auto it = non_deprecated_fields.rbegin();
          it != non_deprecated_fields.rend(); ++it) {
-      auto pair = *it;
-      auto &field = *pair.second;
+      const FieldDef &field = *it->second;
+      const std::string field_name = namer_.Field(field);
 
       if (field.padding) {
         code += "    fbBuilder.pad(" + NumToString(field.padding) + ");\n";
@@ -1082,11 +1026,11 @@
       if (IsStruct(field.value.type)) {
         code += "    ";
         if (prependUnderscore) { code += "_"; }
-        code += field.name + (pack ? ".pack" : ".finish") + "(fbBuilder);\n";
+        code += field_name + (pack ? ".pack" : ".finish") + "(fbBuilder);\n";
       } else {
         code += "    fbBuilder.put" + GenType(field.value.type) + "(";
         if (prependUnderscore) { code += "_"; }
-        code += field.name;
+        code += field_name;
         if (field.value.type.enum_def) { code += ".value"; }
         code += ");\n";
       }
@@ -1106,34 +1050,35 @@
 
     for (auto it = non_deprecated_fields.begin();
          it != non_deprecated_fields.end(); ++it) {
-      auto pair = *it;
-      auto &field = *pair.second;
-      auto offset = pair.first;
+      const FieldDef &field = *it->second;
+      auto offset = it->first;
 
-      std::string field_name = (prependUnderscore ? "_" : "") +
-                               ConvertCase(field.name, Case::kLowerCamel);
+      std::string field_var =
+          (prependUnderscore ? "_" : "") + namer_.Variable(field);
 
       if (IsScalar(field.value.type.base_type)) {
         code += "    fbBuilder.add" + GenType(field.value.type) + "(" +
-                NumToString(offset) + ", " + field_name;
+                NumToString(offset) + ", " + field_var;
         if (field.value.type.enum_def) {
           bool isNullable = getDefaultValue(field.value).empty();
           code += (isNullable || !pack) ? "?.value" : ".value";
         }
         code += ");\n";
       } else if (IsStruct(field.value.type)) {
-        code += "    if (" + field_name + " != null) {\n";
+        code += "    if (" + field_var + " != null) {\n";
         code += "      fbBuilder.addStruct(" + NumToString(offset) + ", " +
-                field_name + (pack ? "!.pack" : "!.finish") + "(fbBuilder));\n";
+                field_var + (pack ? "!.pack" : "!.finish") + "(fbBuilder));\n";
         code += "    }\n";
       } else {
         code += "    fbBuilder.addOffset(" + NumToString(offset) + ", " +
-                ConvertCase(field.name, Case::kLowerCamel) + "Offset);\n";
+                namer_.Variable(field) + "Offset);\n";
       }
     }
     code += "    return fbBuilder.endTable();\n";
     return code;
   }
+
+  const IdlNamer namer_;
 };
 }  // namespace dart
 
@@ -1148,8 +1093,7 @@
   auto filebase =
       flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
   dart::DartGenerator generator(parser, path, file_name);
-  auto make_rule =
-      generator.GeneratedFileName(path, file_name, parser.opts) + ": ";
+  auto make_rule = generator.Filename("") + ": ";
 
   auto included_files = parser.GetIncludedFilesRecursive(file_name);
   for (auto it = included_files.begin(); it != included_files.end(); ++it) {
diff --git a/src/idl_gen_fbs.cpp b/src/idl_gen_fbs.cpp
index 35c1a7d..782557f 100644
--- a/src/idl_gen_fbs.cpp
+++ b/src/idl_gen_fbs.cpp
@@ -82,7 +82,7 @@
       if (it->second.empty())
         continue;
       std::string basename;
-      if(parser.opts.keep_include_path) {
+      if(parser.opts.keep_prefix) {
         basename = flatbuffers::StripExtension(it->second);
       } else {
         basename = flatbuffers::StripPath(
diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp
index 306a022..51e018a 100644
--- a/src/idl_gen_go.cpp
+++ b/src/idl_gen_go.cpp
@@ -23,7 +23,7 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
-#include "namer.h"
+#include "idl_namer.h"
 
 #ifdef _WIN32
 #  include <direct.h>
@@ -38,8 +38,10 @@
 
 namespace go {
 
+namespace {
+
 // see https://golang.org/ref/spec#Keywords
-std::set<std::string> GoKeywords() {
+static std::set<std::string> GoKeywords() {
   return {
     "break",    "default",     "func",   "interface", "select",
     "case",     "defer",       "go",     "map",       "struct",
@@ -49,7 +51,7 @@
   };
 }
 
-Namer::Config GoDefaultConfig() {
+static Namer::Config GoDefaultConfig() {
   // Note that the functions with user defined types in the name use
   // upper camel case for all but the user defined type itself, which is keep
   // cased. Despite being a function, we interpret it as a Type.
@@ -61,6 +63,7 @@
            /*variables=*/Case::kLowerCamel,
            /*variants=*/Case::kKeep,
            /*enum_variant_seperator=*/"",  // I.e. Concatenate.
+           /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
            /*namespaces=*/Case::kKeep,
            /*namespace_seperator=*/"__",
            /*object_prefix=*/"",
@@ -74,6 +77,8 @@
            /*filename_extension=*/".go" };
 }
 
+} // namespace
+
 class GoGenerator : public BaseGenerator {
  public:
   GoGenerator(const Parser &parser, const std::string &path,
@@ -81,8 +86,8 @@
       : BaseGenerator(parser, path, file_name, "" /* not used*/,
                       "" /* not used */, "go"),
         cur_name_space_(nullptr),
-        namer_({ GoDefaultConfig().WithFlagOptions(parser.opts, path),
-                 GoKeywords() }) {
+        namer_(WithFlagOptions(GoDefaultConfig(), parser.opts, path),
+               GoKeywords()) {
     std::istringstream iss(go_namespace);
     std::string component;
     while (std::getline(iss, component, '.')) {
@@ -140,7 +145,7 @@
  private:
   Namespace go_namespace_;
   Namespace *cur_name_space_;
-  const Namer namer_;
+  const IdlNamer namer_;
 
   struct NamespacePtrLess {
     bool operator()(const Namespace *a, const Namespace *b) const {
@@ -160,7 +165,7 @@
   void BeginClass(const StructDef &struct_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
 
-    code += "type " + namer_.Type(struct_def.name) + " struct {\n\t";
+    code += "type " + namer_.Type(struct_def) + " struct {\n\t";
 
     // _ is reserved in flatbuffers field names, so no chance of name conflict:
     code += "_tab ";
@@ -171,7 +176,7 @@
   // Construct the name of the type for this enum.
   std::string GetEnumTypeName(const EnumDef &enum_def) {
     return WrapInNameSpaceAndTrack(enum_def.defined_namespace,
-                                   namer_.Type(enum_def.name));
+                                   namer_.Type(enum_def));
   }
 
   // Create a type for the enum values.
@@ -192,7 +197,7 @@
                   size_t max_name_length, std::string *code_ptr) {
     std::string &code = *code_ptr;
     code += "\t";
-    code += namer_.EnumVariant(enum_def.name, ev.name);
+    code += namer_.EnumVariant(enum_def, ev);
     code += " ";
     code += std::string(max_name_length - ev.name.length(), ' ');
     code += GetEnumTypeName(enum_def);
@@ -219,7 +224,7 @@
                       size_t max_name_length, std::string *code_ptr) {
     std::string &code = *code_ptr;
     code += "\t";
-    code += namer_.EnumVariant(enum_def.name, ev.name);
+    code += namer_.EnumVariant(enum_def, ev);
     code += ": ";
     code += std::string(max_name_length - ev.name.length(), ' ');
     code += "\"";
@@ -236,7 +241,7 @@
   // Generate String() method on enum type.
   void EnumStringer(const EnumDef &enum_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
-    const std::string enum_type = namer_.Type(enum_def.name);
+    const std::string enum_type = namer_.Type(enum_def);
     code += "func (v " + enum_type + ") String() string {\n";
     code += "\tif s, ok := EnumNames" + enum_type + "[v]; ok {\n";
     code += "\t\treturn s\n";
@@ -250,7 +255,7 @@
   void BeginEnumValues(const EnumDef &enum_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
     code += "var EnumValues";
-    code += namer_.Type(enum_def.name);
+    code += namer_.Type(enum_def);
     code += " = map[string]" + GetEnumTypeName(enum_def) + "{\n";
   }
 
@@ -262,7 +267,7 @@
     code += ev.name;
     code += "\": ";
     code += std::string(max_name_length - ev.name.length(), ' ');
-    code += namer_.EnumVariant(enum_def.name, ev.name);
+    code += namer_.EnumVariant(enum_def, ev);
     code += ",\n";
   }
 
@@ -277,7 +282,7 @@
                              std::string *code_ptr) {
     std::string &code = *code_ptr;
     const std::string size_prefix[] = { "", "SizePrefixed" };
-    const std::string struct_type = namer_.Type(struct_def.name);
+    const std::string struct_type = namer_.Type(struct_def);
 
     for (int i = 0; i < 2; i++) {
       code += "func Get" + size_prefix[i] + "RootAs" + struct_type;
@@ -336,7 +341,7 @@
     std::string &code = *code_ptr;
 
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name) + "Length(";
+    code += " " + namer_.Function(field) + "Length(";
     code += ") int " + OffsetPrefix(field);
     code += "\t\treturn rcv._tab.VectorLen(o)\n\t}\n";
     code += "\treturn 0\n}\n\n";
@@ -348,7 +353,7 @@
     std::string &code = *code_ptr;
 
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name) + "Bytes(";
+    code += " " + namer_.Function(field) + "Bytes(";
     code += ") []byte " + OffsetPrefix(field);
     code += "\t\treturn rcv._tab.ByteVector(o + rcv._tab.Pos)\n\t}\n";
     code += "\treturn nil\n}\n\n";
@@ -360,7 +365,7 @@
     std::string &code = *code_ptr;
     std::string getter = GenGetter(field.value.type);
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name);
+    code += " " + namer_.Function(field);
     code += "() " + TypeName(field) + " {\n";
     code += "\treturn " +
             CastToEnum(field.value.type,
@@ -375,7 +380,7 @@
     std::string &code = *code_ptr;
     std::string getter = GenGetter(field.value.type);
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name);
+    code += " " + namer_.Function(field);
     code += "() " + TypeName(field) + " ";
     code += OffsetPrefix(field);
     if (field.IsScalarOptional()) {
@@ -384,9 +389,7 @@
       code += "\t\treturn ";
     }
     code += CastToEnum(field.value.type, getter + "(o + rcv._tab.Pos)");
-    if (field.IsScalarOptional()) {
-      code += "\n\t\treturn &v";
-    }
+    if (field.IsScalarOptional()) { code += "\n\t\treturn &v"; }
     code += "\n\t}\n";
     code += "\treturn " + GenConstant(field) + "\n";
     code += "}\n\n";
@@ -398,7 +401,7 @@
                               const FieldDef &field, std::string *code_ptr) {
     std::string &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name);
+    code += " " + namer_.Function(field);
     code += "(obj *" + TypeName(field);
     code += ") *" + TypeName(field);
     code += " {\n";
@@ -417,7 +420,7 @@
                              std::string *code_ptr) {
     std::string &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name);
+    code += " " + namer_.Function(field);
     code += "(obj *";
     code += TypeName(field);
     code += ") *" + TypeName(field) + " " + OffsetPrefix(field);
@@ -439,7 +442,7 @@
                       std::string *code_ptr) {
     std::string &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name);
+    code += " " + namer_.Function(field);
     code += "() " + TypeName(field) + " ";
     code += OffsetPrefix(field) + "\t\treturn " + GenGetter(field.value.type);
     code += "(o + rcv._tab.Pos)\n\t}\n\treturn nil\n";
@@ -451,7 +454,7 @@
                      std::string *code_ptr) {
     std::string &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name) + "(";
+    code += " " + namer_.Function(field) + "(";
     code += "obj " + GenTypePointer(field.value.type) + ") bool ";
     code += OffsetPrefix(field);
     code += "\t\t" + GenGetter(field.value.type);
@@ -467,7 +470,7 @@
     auto vectortype = field.value.type.VectorType();
 
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name);
+    code += " " + namer_.Function(field);
     code += "(obj *" + TypeName(field);
     code += ", j int) bool " + OffsetPrefix(field);
     code += "\t\tx := rcv._tab.Vector(o)\n";
@@ -490,7 +493,7 @@
     auto vectortype = field.value.type.VectorType();
 
     GenReceiver(struct_def, code_ptr);
-    code += " " + namer_.Function(field.name);
+    code += " " + namer_.Function(field);
     code += "(j int) " + TypeName(field) + " ";
     code += OffsetPrefix(field);
     code += "\t\ta := rcv._tab.Vector(o)\n";
@@ -538,7 +541,7 @@
       } else {
         std::string &code = *code_ptr;
         code += std::string(", ") + nameprefix;
-        code += namer_.Variable(field.name);
+        code += namer_.Variable(field);
         code += " " + TypeName(field);
       }
     }
@@ -568,7 +571,7 @@
       } else {
         code += "\tbuilder.Prepend" + GenMethod(field) + "(";
         code += CastToBaseType(field.value.type,
-                               nameprefix + namer_.Variable(field.name)) +
+                               nameprefix + namer_.Variable(field)) +
                 ")\n";
       }
     }
@@ -583,7 +586,7 @@
   // Get the value of a table's starting offset.
   void GetStartOfTable(const StructDef &struct_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
-    code += "func " + namer_.Type(struct_def.name) + "Start";
+    code += "func " + namer_.Type(struct_def) + "Start";
     code += "(builder *flatbuffers.Builder) {\n";
     code += "\tbuilder.StartObject(";
     code += NumToString(struct_def.fields.vec.size());
@@ -594,9 +597,8 @@
   void BuildFieldOfTable(const StructDef &struct_def, const FieldDef &field,
                          const size_t offset, std::string *code_ptr) {
     std::string &code = *code_ptr;
-    const std::string field_var = namer_.Variable(field.name);
-    code += "func " + namer_.Type(struct_def.name) + "Add" +
-            namer_.Function(field.name);
+    const std::string field_var = namer_.Variable(field);
+    code += "func " + namer_.Type(struct_def) + "Add" + namer_.Function(field);
     code += "(builder *flatbuffers.Builder, ";
     code += field_var + " ";
     if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) {
@@ -632,8 +634,8 @@
   void BuildVectorOfTable(const StructDef &struct_def, const FieldDef &field,
                           std::string *code_ptr) {
     std::string &code = *code_ptr;
-    code += "func " + namer_.Type(struct_def.name) + "Start";
-    code += namer_.Function(field.name);
+    code += "func " + namer_.Type(struct_def) + "Start";
+    code += namer_.Function(field);
     code += "Vector(builder *flatbuffers.Builder, numElems int) ";
     code += "flatbuffers.UOffsetT {\n\treturn builder.StartVector(";
     auto vector_type = field.value.type.VectorType();
@@ -647,7 +649,7 @@
   // Get the offset of the end of a table.
   void GetEndOffsetOnTable(const StructDef &struct_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
-    code += "func " + namer_.Type(struct_def.name) + "End";
+    code += "func " + namer_.Type(struct_def) + "End";
     code += "(builder *flatbuffers.Builder) flatbuffers.UOffsetT ";
     code += "{\n\treturn builder.EndObject()\n}\n";
   }
@@ -655,7 +657,7 @@
   // Generate the receiver for function signatures.
   void GenReceiver(const StructDef &struct_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
-    code += "func (rcv *" + namer_.Type(struct_def.name) + ")";
+    code += "func (rcv *" + namer_.Type(struct_def) + ")";
   }
 
   // Generate a struct field getter, conditioned on its child type(s).
@@ -708,8 +710,9 @@
     std::string setter =
         "rcv._tab.Mutate" + namer_.Method(GenTypeBasic(field.value.type));
     GenReceiver(struct_def, code_ptr);
-    code += " Mutate" + namer_.Function(field.name);
-    code += "(n " + GenTypeGet(field.value.type) + ") bool {\n\treturn " + setter;
+    code += " Mutate" + namer_.Function(field);
+    code +=
+        "(n " + GenTypeGet(field.value.type) + ") bool {\n\treturn " + setter;
     code += "(rcv._tab.Pos+flatbuffers.UOffsetT(";
     code += NumToString(field.value.offset) + "), ";
     code += CastToBaseType(field.value.type, "n") + ")\n}\n\n";
@@ -722,7 +725,7 @@
     std::string setter = "rcv._tab.Mutate" +
                          namer_.Method(GenTypeBasic(field.value.type)) + "Slot";
     GenReceiver(struct_def, code_ptr);
-    code += " Mutate" + namer_.Function(field.name);
+    code += " Mutate" + namer_.Function(field);
     code += "(n " + GenTypeGet(field.value.type) + ") bool {\n\treturn ";
     code += setter + "(" + NumToString(field.value.offset) + ", ";
     code += CastToBaseType(field.value.type, "n") + ")\n";
@@ -738,7 +741,7 @@
     std::string setter =
         "rcv._tab.Mutate" + namer_.Method(GenTypeBasic(vectortype));
     GenReceiver(struct_def, code_ptr);
-    code += " Mutate" + namer_.Function(field.name);
+    code += " Mutate" + namer_.Function(field);
     code += "(j int, n " + TypeName(field) + ") bool ";
     code += OffsetPrefix(field);
     code += "\t\ta := rcv._tab.Vector(o)\n";
@@ -841,11 +844,10 @@
           field.value.type.enum_def != nullptr &&
           field.value.type.enum_def->is_union)
         continue;
-      code += "\t" + namer_.Field(field.name) + " ";
-      if (field.IsScalarOptional()) {
-        code += "*";
-      }
-      code += NativeType(field.value.type) + "\n";
+      code += "\t" + namer_.Field(field) + " ";
+      if (field.IsScalarOptional()) { code += "*"; }
+      code += NativeType(field.value.type) + " `json:\"" + field.name + "\"`" +
+              "\n";
     }
     code += "}\n\n";
 
@@ -861,7 +863,7 @@
   void GenNativeUnion(const EnumDef &enum_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
     code += "type " + NativeName(enum_def) + " struct {\n";
-    code += "\tType " + namer_.Type(enum_def.name) + "\n";
+    code += "\tType " + namer_.Type(enum_def) + "\n";
     code += "\tValue interface{}\n";
     code += "}\n\n";
   }
@@ -877,7 +879,7 @@
          ++it2) {
       const EnumVal &ev = **it2;
       if (ev.IsZero()) continue;
-      code += "\tcase " + namer_.EnumVariant(enum_def.name, ev.name) + ":\n";
+      code += "\tcase " + namer_.EnumVariant(enum_def, ev) + ":\n";
       code += "\t\treturn t.Value.(" + NativeType(ev.union_type) +
               ").Pack(builder)\n";
     }
@@ -889,7 +891,7 @@
   void GenNativeUnionUnPack(const EnumDef &enum_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
 
-    code += "func (rcv " + namer_.Type(enum_def.name) +
+    code += "func (rcv " + namer_.Type(enum_def) +
             ") UnPack(table flatbuffers.Table) *" + NativeName(enum_def) +
             " {\n";
     code += "\tswitch rcv {\n";
@@ -898,13 +900,13 @@
          ++it2) {
       const EnumVal &ev = **it2;
       if (ev.IsZero()) continue;
-      code += "\tcase " + namer_.EnumVariant(enum_def.name, ev.name) + ":\n";
+      code += "\tcase " + namer_.EnumVariant(enum_def, ev) + ":\n";
       code += "\t\tx := " + ev.union_type.struct_def->name + "{_tab: table}\n";
 
       code += "\t\treturn &" +
               WrapInNameSpaceAndTrack(enum_def.defined_namespace,
                                       NativeName(enum_def)) +
-              "{ Type: " + namer_.EnumVariant(enum_def.name, ev.name) +
+              "{ Type: " + namer_.EnumVariant(enum_def, ev) +
               ", Value: x.UnPack() }\n";
     }
     code += "\t}\n";
@@ -914,7 +916,7 @@
 
   void GenNativeTablePack(const StructDef &struct_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
-    const std::string struct_type = namer_.Type(struct_def.name);
+    const std::string struct_type = namer_.Type(struct_def);
 
     code += "func (t *" + NativeName(struct_def) +
             ") Pack(builder *flatbuffers.Builder) flatbuffers.UOffsetT {\n";
@@ -925,8 +927,8 @@
       if (field.deprecated) continue;
       if (IsScalar(field.value.type.base_type)) continue;
 
-      const std::string field_field = namer_.Field(field.name);
-      const std::string field_var = namer_.Variable(field.name);
+      const std::string field_field = namer_.Field(field);
+      const std::string field_var = namer_.Variable(field);
       const std::string offset = field_var + "Offset";
 
       if (IsString(field.value.type)) {
@@ -962,7 +964,7 @@
                   "[j].Pack(builder)\n";
           code += "\t\t}\n";
         }
-        code += "\t\t" + struct_type + "Start" + namer_.Function(field.name) +
+        code += "\t\t" + struct_type + "Start" + namer_.Function(field) +
                 "Vector(builder, " + length + ")\n";
         code += "\t\tfor j := " + length + " - 1; j >= 0; j-- {\n";
         if (IsScalar(field.value.type.element)) {
@@ -996,9 +998,9 @@
          it != struct_def.fields.vec.end(); ++it) {
       const FieldDef &field = **it;
       if (field.deprecated) continue;
-      const std::string field_field = namer_.Field(field.name);
-      const std::string field_fn = namer_.Function(field.name);
-      const std::string offset = namer_.Variable(field.name) + "Offset";
+      const std::string field_field = namer_.Field(field);
+      const std::string field_fn = namer_.Function(field);
+      const std::string offset = namer_.Variable(field) + "Offset";
 
       if (IsScalar(field.value.type.base_type)) {
         std::string prefix;
@@ -1011,9 +1013,7 @@
           code += "\t" + struct_type + "Add" + field_fn + "(builder, " +
                   prefix + "t." + field_field + ")\n";
         }
-        if (field.IsScalarOptional()) {
-          code += "\t}\n";
-        }
+        if (field.IsScalarOptional()) { code += "\t}\n"; }
       } else {
         if (field.value.type.base_type == BASE_TYPE_STRUCT &&
             field.value.type.struct_def->fixed) {
@@ -1037,7 +1037,7 @@
   void GenNativeTableUnPack(const StructDef &struct_def,
                             std::string *code_ptr) {
     std::string &code = *code_ptr;
-    const std::string struct_type = namer_.Type(struct_def.name);
+    const std::string struct_type = namer_.Type(struct_def);
 
     code += "func (rcv *" + struct_type + ") UnPackTo(t *" +
             NativeName(struct_def) + ") {\n";
@@ -1045,8 +1045,8 @@
          it != struct_def.fields.vec.end(); ++it) {
       const FieldDef &field = **it;
       if (field.deprecated) continue;
-      const std::string field_field = namer_.Field(field.name);
-      const std::string field_var = namer_.Variable(field.name);
+      const std::string field_field = namer_.Field(field);
+      const std::string field_var = namer_.Variable(field);
       const std::string length = field_var + "Length";
       if (IsScalar(field.value.type.base_type)) {
         if (field.value.type.enum_def != nullptr &&
@@ -1089,8 +1089,8 @@
       } else if (field.value.type.base_type == BASE_TYPE_UNION) {
         const std::string field_table = field_var + "Table";
         code += "\t" + field_table + " := flatbuffers.Table{}\n";
-        code += "\tif rcv." + namer_.Method(field.name) + "(&" + field_table +
-                ") {\n";
+        code +=
+            "\tif rcv." + namer_.Method(field) + "(&" + field_table + ") {\n";
         code += "\t\tt." + field_field + " = rcv." +
                 namer_.Method(field.name + UnionTypeFieldSuffix()) +
                 "().UnPack(" + field_table + ")\n";
@@ -1116,7 +1116,7 @@
     code += "func (t *" + NativeName(struct_def) +
             ") Pack(builder *flatbuffers.Builder) flatbuffers.UOffsetT {\n";
     code += "\tif t == nil { return 0 }\n";
-    code += "\treturn Create" + namer_.Type(struct_def.name) + "(builder";
+    code += "\treturn Create" + namer_.Type(struct_def) + "(builder";
     StructPackArgs(struct_def, "", code_ptr);
     code += ")\n";
     code += "}\n";
@@ -1130,10 +1130,10 @@
       const FieldDef &field = **it;
       if (field.value.type.base_type == BASE_TYPE_STRUCT) {
         StructPackArgs(*field.value.type.struct_def,
-                       (nameprefix + namer_.Field(field.name) + ".").c_str(),
+                       (nameprefix + namer_.Field(field) + ".").c_str(),
                        code_ptr);
       } else {
-        code += std::string(", t.") + nameprefix + namer_.Field(field.name);
+        code += std::string(", t.") + nameprefix + namer_.Field(field);
       }
     }
   }
@@ -1142,22 +1142,22 @@
                              std::string *code_ptr) {
     std::string &code = *code_ptr;
 
-    code += "func (rcv *" + namer_.Type(struct_def.name) + ") UnPackTo(t *" +
+    code += "func (rcv *" + namer_.Type(struct_def) + ") UnPackTo(t *" +
             NativeName(struct_def) + ") {\n";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
       const FieldDef &field = **it;
       if (field.value.type.base_type == BASE_TYPE_STRUCT) {
-        code += "\tt." + namer_.Field(field.name) + " = rcv." +
-                namer_.Method(field.name) + "(nil).UnPack()\n";
+        code += "\tt." + namer_.Field(field) + " = rcv." +
+                namer_.Method(field) + "(nil).UnPack()\n";
       } else {
-        code += "\tt." + namer_.Field(field.name) + " = rcv." +
-                namer_.Method(field.name) + "()\n";
+        code += "\tt." + namer_.Field(field) + " = rcv." +
+                namer_.Method(field) + "()\n";
       }
     }
     code += "}\n\n";
 
-    code += "func (rcv *" + namer_.Type(struct_def.name) + ") UnPack() *" +
+    code += "func (rcv *" + namer_.Type(struct_def) + ") UnPack() *" +
             NativeName(struct_def) + " {\n";
     code += "\tif rcv == nil { return nil }\n";
     code += "\tt := &" + NativeName(struct_def) + "{}\n";
@@ -1247,9 +1247,7 @@
 
   std::string TypeName(const FieldDef &field) {
     std::string prefix;
-    if (field.IsScalarOptional()) {
-      prefix = "*";
-    }
+    if (field.IsScalarOptional()) { prefix = "*"; }
     return prefix + GenTypeGet(field.value.type);
   }
 
@@ -1274,9 +1272,7 @@
   }
 
   std::string GenConstant(const FieldDef &field) {
-    if (field.IsScalarOptional()) {
-      return "nil";
-    }
+    if (field.IsScalarOptional()) { return "nil"; }
     switch (field.value.type.base_type) {
       case BASE_TYPE_BOOL:
         return field.value.constant == "0" ? "false" : "true";
@@ -1285,11 +1281,11 @@
   }
 
   std::string NativeName(const StructDef &struct_def) const {
-    return namer_.ObjectType(struct_def.name);
+    return namer_.ObjectType(struct_def);
   }
 
   std::string NativeName(const EnumDef &enum_def) const {
-    return namer_.ObjectType(enum_def.name);
+    return namer_.ObjectType(enum_def);
   }
 
   std::string NativeType(const Type &type) {
@@ -1366,20 +1362,21 @@
     while (code.length() > 2 && code.substr(code.length() - 2) == "\n\n") {
       code.pop_back();
     }
-    std::string filename = namer_.Directories(ns.components) +
-                           namer_.File(def.name, SkipFile::Suffix);
+    std::string directory = namer_.Directories(ns);
+    std::string file = namer_.File(def, SkipFile::Suffix);
+    EnsureDirExists(directory);
+    std::string filename = directory + file;
     return SaveFile(filename.c_str(), code, false);
   }
 
   // Create the full name of the imported namespace (format: A__B__C).
   std::string NamespaceImportName(const Namespace *ns) const {
-    return namer_.Namespace(ns->components);
+    return namer_.Namespace(*ns);
   }
 
   // Create the full path for the imported namespace (format: A/B/C).
   std::string NamespaceImportPath(const Namespace *ns) const {
-    return namer_.Directories(ns->components,
-                              SkipDir::OutputPathAndTrailingPathSeparator);
+    return namer_.Directories(*ns, SkipDir::OutputPathAndTrailingPathSeparator);
   }
 
   // Ensure that a type is prefixed with its go package import name if it is
diff --git a/src/idl_gen_grpc.cpp b/src/idl_gen_grpc.cpp
index 6894ffb..2be24e7 100644
--- a/src/idl_gen_grpc.cpp
+++ b/src/idl_gen_grpc.cpp
@@ -242,12 +242,6 @@
     return StripExtension(file_name_);
   }
 
-  std::string message_header_ext() const {
-    return parser_.opts.filename_suffix + ".h";
-  }
-
-  std::string service_header_ext() const { return ".grpc.fb.h"; }
-
   std::string package() const {
     return parser_.current_namespace_->GetFullyQualifiedName("");
   }
@@ -347,6 +341,7 @@
 
 bool GenerateCppGRPC(const Parser &parser, const std::string &path,
                      const std::string &file_name) {
+  const auto &opts = parser.opts;
   int nservices = 0;
   for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
        ++it) {
@@ -354,9 +349,15 @@
   }
   if (!nservices) return true;
 
+  std::string suffix = "";
+  suffix += opts.filename_suffix.empty() ? "_generated" : opts.filename_suffix;
+  suffix += ".";
+  suffix += opts.filename_extension.empty() ? "h" : opts.filename_extension;
+
   grpc_cpp_generator::Parameters generator_parameters;
   // TODO(wvo): make the other parameters in this struct configurable.
   generator_parameters.use_system_headers = true;
+  generator_parameters.message_header_extension = suffix;
 
   FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
 
diff --git a/src/idl_gen_java.cpp b/src/idl_gen_java.cpp
index ee09391..6ee97aa 100644
--- a/src/idl_gen_java.cpp
+++ b/src/idl_gen_java.cpp
@@ -20,20 +20,65 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
+#include "idl_namer.h"
 
 namespace flatbuffers {
 namespace java {
 
-static TypedFloatConstantGenerator JavaFloatGen("Double.", "Float.", "NaN",
+namespace {
+
+static Namer::Config JavaDefaultConfig() {
+  return {
+    /*types=*/Case::kKeep,
+    /*constants=*/Case::kScreamingSnake,
+    /*methods=*/Case::kLowerCamel,
+    /*functions=*/Case::kLowerCamel,
+    /*fields=*/Case::kLowerCamel,
+    /*variables=*/Case::kLowerCamel,
+    /*variants=*/Case::kKeep,
+    /*enum_variant_seperator=*/".",
+    /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
+    /*namespaces=*/Case::kKeep,
+    /*namespace_seperator=*/".",
+    /*object_prefix=*/"",
+    /*object_suffix=*/"T",
+    /*keyword_prefix=*/"",
+    /*keyword_suffix=*/"_",
+    /*filenames=*/Case::kKeep,
+    /*directories=*/Case::kKeep,
+    /*output_path=*/"",
+    /*filename_suffix=*/"_generated",
+    /*filename_extension=*/".java",
+  };
+}
+
+static std::set<std::string> JavaKeywords() {
+  return {
+    "abstract", "continue", "for",        "new",       "switch",
+    "assert",   "default",  "goto",       "package",   "synchronized",
+    "boolean",  "do",       "if",         "private",   "this",
+    "break",    "double",   "implements", "protected", "throw",
+    "byte",     "else",     "import",     "public",    "throws",
+    "case",     "enum",     "instanceof", "return",    "transient",
+    "catch",    "extends",  "int",        "short",     "try",
+    "char",     "final",    "interface",  "static",    "void",
+    "class",    "finally",  "long",       "strictfp",  "volatile",
+    "const",    "float",    "native",     "super",     "while",
+  };
+}
+
+static const TypedFloatConstantGenerator JavaFloatGen("Double.", "Float.", "NaN",
                                                 "POSITIVE_INFINITY",
                                                 "NEGATIVE_INFINITY");
 
-static CommentConfig comment_config = {
+static const CommentConfig comment_config = {
   "/**",
   " *",
   " */",
 };
 
+} // namespace
+
 class JavaGenerator : public BaseGenerator {
   struct FieldArrayLength {
     std::string name;
@@ -44,7 +89,9 @@
   JavaGenerator(const Parser &parser, const std::string &path,
                 const std::string &file_name)
       : BaseGenerator(parser, path, file_name, "", ".", "java"),
-        cur_name_space_(nullptr) {}
+        cur_name_space_(nullptr),
+        namer_(WithFlagOptions(JavaDefaultConfig(), parser.opts, path),
+               JavaKeywords()) {}
 
   JavaGenerator &operator=(const JavaGenerator &);
   bool generate() {
@@ -56,7 +103,7 @@
       std::string enumcode;
       auto &enum_def = **it;
       if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
-      GenEnum(enum_def, &enumcode);
+      GenEnum(enum_def, enumcode);
       if (parser_.opts.one_file) {
         one_file_code += enumcode;
       } else {
@@ -67,8 +114,8 @@
 
       if (parser_.opts.generate_object_based_api && enum_def.is_union) {
         enumcode = "";
-        GenEnum_ObjectAPI(enum_def, &enumcode, parser_.opts);
-        auto class_name = enum_def.name + "Union";
+        GenEnum_ObjectAPI(enum_def, enumcode);
+        auto class_name = namer_.Type(enum_def) + "Union";
         if (parser_.opts.one_file) {
           one_file_code += enumcode;
         } else {
@@ -85,7 +132,7 @@
       auto &struct_def = **it;
       if (!parser_.opts.one_file)
         cur_name_space_ = struct_def.defined_namespace;
-      GenStruct(struct_def, &declcode, parser_.opts);
+      GenStruct(struct_def, declcode, parser_.opts);
       if (parser_.opts.one_file) {
         one_file_code += declcode;
       } else {
@@ -96,8 +143,8 @@
 
       if (parser_.opts.generate_object_based_api) {
         declcode = "";
-        GenStruct_ObjectAPI(struct_def, &declcode, parser_.opts);
-        auto class_name = GenTypeName_ObjectAPI(struct_def.name, parser_.opts);
+        GenStruct_ObjectAPI(struct_def, declcode);
+        auto class_name = namer_.ObjectType(struct_def);
         if (parser_.opts.one_file) {
           one_file_code += declcode;
         } else {
@@ -124,7 +171,7 @@
     std::string code;
     code = "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
 
-    std::string namespace_name = FullNamespace(".", ns);
+    const std::string namespace_name = FullNamespace(".", ns);
     if (!namespace_name.empty()) {
       code += "package " + namespace_name + ";";
       code += "\n\n";
@@ -144,7 +191,10 @@
 
     code += classcode;
     if (!namespace_name.empty()) code += "";
-    auto filename = NamespaceDir(ns) + defname + ".java";
+    const std::string dirs = namer_.Directories(ns);
+    EnsureDirExists(dirs);
+    const std::string filename =
+        dirs + namer_.File(defname, /*skips=*/SkipFile::Suffix);
     return SaveFile(filename.c_str(), code, false);
   }
 
@@ -181,7 +231,7 @@
     switch (type.base_type) {
       case BASE_TYPE_STRING: return "String";
       case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
-      case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def);
+      case BASE_TYPE_STRUCT: return namer_.NamespacedType(*type.struct_def);
       case BASE_TYPE_UNION: FLATBUFFERS_FALLTHROUGH();  // else fall thru
       default: return "Table";
     }
@@ -285,7 +335,7 @@
     FLATBUFFERS_ASSERT(value.type.enum_def);
     auto &enum_def = *value.type.enum_def;
     auto enum_val = enum_def.FindByValue(value.constant);
-    return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
+    return enum_val ? namer_.NamespacedEnumVariant(enum_def, *enum_val)
                     : value.constant;
   }
 
@@ -320,8 +370,7 @@
     return GenDefaultValue(field);
   }
 
-  void GenEnum(EnumDef &enum_def, std::string *code_ptr) const {
-    std::string &code = *code_ptr;
+  void GenEnum(EnumDef &enum_def, std::string &code) const {
     if (enum_def.generated) return;
 
     // Generate enum definitions of the form:
@@ -329,7 +378,7 @@
     // In Java, we use ints rather than the Enum feature, because we want them
     // to map directly to how they're used in C/C++ and file formats.
     // That, and Java Enums are expensive, and not universally liked.
-    GenComment(enum_def.doc_comment, code_ptr, &comment_config);
+    GenComment(enum_def.doc_comment, &code, &comment_config);
 
     code += "@SuppressWarnings(\"unused\")\n";
     if (enum_def.attributes.Lookup("private")) {
@@ -337,16 +386,16 @@
     } else {
       code += "public ";
     }
-    code += "final class " + enum_def.name;
+    code += "final class " + namer_.Type(enum_def);
     code += " {\n";
-    code += "  private " + enum_def.name + "() { }\n";
+    code += "  private " + namer_.Type(enum_def) + "() { }\n";
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       auto &ev = **it;
-      GenComment(ev.doc_comment, code_ptr, &comment_config, "  ");
+      GenComment(ev.doc_comment, &code, &comment_config, "  ");
       code += "  public static final ";
       code += GenTypeBasic(DestinationType(enum_def.underlying_type, false));
       code += " ";
-      code += ev.name + " = ";
+      code += namer_.Variant(ev) + " = ";
       code += enum_def.ToString(ev);
       code += ";\n";
     }
@@ -364,13 +413,14 @@
             "long") {
       code += "\n  public static final String";
       code += "[] names = { ";
-      auto val = enum_def.Vals().front();
+      const EnumVal *prev = enum_def.Vals().front();
       for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
            ++it) {
-        auto ev = *it;
-        for (auto k = enum_def.Distance(val, ev); k > 1; --k) code += "\"\", ";
-        val = ev;
-        code += "\"" + (*it)->name + "\", ";
+        const EnumVal &ev = **it;
+        for (auto k = enum_def.Distance(prev, &ev); k > 1; --k)
+          code += "\"\", ";
+        prev = &ev;
+        code += "\"" + namer_.Variant(ev) + "\", ";
       }
       code += "};\n\n";
       code += "  public static ";
@@ -378,7 +428,7 @@
       code += " name";
       code += "(int e) { return names[e";
       if (enum_def.MinValue()->IsNonZero())
-        code += " - " + enum_def.MinValue()->name;
+        code += " - " + namer_.Variant(enum_def.MinValue()->name);
       code += "]; }\n";
     }
 
@@ -445,9 +495,8 @@
 
   // Recursively generate arguments for a constructor, to deal with nested
   // structs.
-  void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
+  void GenStructArgs(const StructDef &struct_def, std::string &code,
                      const char *nameprefix, size_t array_count = 0) const {
-    std::string &code = *code_ptr;
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
       auto &field = **it;
@@ -460,7 +509,7 @@
         // Generate arguments for a struct inside a struct. To ensure names
         // don't clash, and to make it obvious these arguments are constructing
         // a nested struct, prefix the name with the field name.
-        GenStructArgs(*field_type.struct_def, code_ptr,
+        GenStructArgs(*field_type.struct_def, code,
                       (nameprefix + (field.name + "_")).c_str(), array_cnt);
       } else {
         code += ", ";
@@ -468,7 +517,7 @@
         for (size_t i = 0; i < array_cnt; i++) code += "[]";
         code += " ";
         code += nameprefix;
-        code += ConvertCase(field.name, Case::kLowerCamel);
+        code += namer_.Field(field);
       }
     }
   }
@@ -476,10 +525,9 @@
   // Recusively generate struct construction statements of the form:
   // builder.putType(name);
   // and insert manual padding.
-  void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
+  void GenStructBody(const StructDef &struct_def, std::string &code,
                      const char *nameprefix, size_t index = 0,
                      bool in_array = false) const {
-    std::string &code = *code_ptr;
     std::string indent((index + 1) * 2, ' ');
     code += indent + "  builder.prep(";
     code += NumToString(struct_def.minalign) + ", ";
@@ -493,7 +541,7 @@
         code += NumToString(field.padding) + ");\n";
       }
       if (IsStruct(field_type)) {
-        GenStructBody(*field_type.struct_def, code_ptr,
+        GenStructBody(*field_type.struct_def, code,
                       (nameprefix + (field.name + "_")).c_str(), index,
                       in_array);
       } else {
@@ -507,7 +555,7 @@
           in_array = true;
         }
         if (IsStruct(type)) {
-          GenStructBody(*field_type.struct_def, code_ptr,
+          GenStructBody(*field_type.struct_def, code,
                         (nameprefix + (field.name + "_")).c_str(), index + 1,
                         in_array);
         } else {
@@ -515,8 +563,7 @@
           code += indent + "  builder.put";
           code += GenMethod(type) + "(";
           code += SourceCast(type);
-          auto argname =
-              nameprefix + ConvertCase(field.name, Case::kLowerCamel);
+          auto argname = nameprefix + namer_.Variable(field);
           code += argname;
           size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
           for (size_t i = 0; in_array && i < array_cnt; i++) {
@@ -529,12 +576,6 @@
     }
   }
 
-  std::string GenByteBufferLength(const char *bb_name) const {
-    std::string bb_len = bb_name;
-    bb_len += ".capacity()";
-    return bb_len;
-  }
-
   std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
                               const char *num = nullptr) const {
     std::string key_offset = "";
@@ -543,7 +584,7 @@
       key_offset += num;
       key_offset += ", _bb)";
     } else {
-      key_offset += GenByteBufferLength("bb");
+      key_offset += "bb.capacity()";
       key_offset += " - tableOffset, bb)";
     }
     return key_offset;
@@ -592,10 +633,9 @@
     return key_getter;
   }
 
-  void GenStruct(StructDef &struct_def, std::string *code_ptr,
+  void GenStruct(StructDef &struct_def, std::string &code,
                  const IDLOptions &opts) const {
     if (struct_def.generated) return;
-    std::string &code = *code_ptr;
 
     // Generate a struct accessor class, with methods of the form:
     // public type name() { return bb.getType(i + offset); }
@@ -603,7 +643,7 @@
     // public type name() {
     //   int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
     // }
-    GenComment(struct_def.doc_comment, code_ptr, &comment_config);
+    GenComment(struct_def.doc_comment, &code, &comment_config);
 
     if (parser_.opts.gen_generated) {
       code += "@javax.annotation.Generated(value=\"flatc\")\n";
@@ -614,33 +654,35 @@
     } else {
       code += "public ";
     }
-    code += "final class " + struct_def.name;
+    const auto struct_class = namer_.Type(struct_def);
+    code += "final class " + struct_class;
     code += " extends ";
     code += struct_def.fixed ? "Struct" : "Table";
     code += " {\n";
 
     if (!struct_def.fixed) {
-      // Generate verson check method.
+      // Generate version check method.
       // Force compile time error if not using the same version runtime.
       code += "  public static void ValidateVersion() {";
       code += " Constants.";
-      code += "FLATBUFFERS_2_0_0(); ";
+      code += "FLATBUFFERS_2_0_8(); ";
       code += "}\n";
 
       // Generate a special accessor for the table that when used as the root
       // of a FlatBuffer
-      std::string method_name = "getRootAs" + struct_def.name;
-      std::string method_signature =
-          "  public static " + struct_def.name + " " + method_name;
+      const std::string method_name =
+          namer_.LegacyJavaMethod2("getRootAs", struct_def, "");
+      const std::string method_signature =
+          "  public static " + struct_class + " " + method_name;
 
       // create convenience method that doesn't require an existing object
       code += method_signature + "(ByteBuffer _bb) ";
-      code += "{ return " + method_name + "(_bb, new " + struct_def.name +
-              "()); }\n";
+      code +=
+          "{ return " + method_name + "(_bb, new " + struct_class + "()); }\n";
 
       // create method that allows object reuse
       code +=
-          method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
+          method_signature + "(ByteBuffer _bb, " + struct_class + " obj) { ";
       code += "_bb.order(ByteOrder.LITTLE_ENDIAN); ";
       code += "return (obj.__assign(_bb.getInt(_bb.";
       code += "position()";
@@ -651,8 +693,10 @@
         if (parser_.file_identifier_.length()) {
           // Check if a buffer has the identifier.
           code += "  public static ";
-          code += "boolean " + struct_def.name;
-          code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
+          code += "boolean " +
+                  namer_.LegacyJavaMethod2(
+                      "", struct_def, "BufferHasIdentifier(ByteBuffer _bb)") +
+                  " { return ";
           code += "__has_identifier(_bb, \"";
           code += parser_.file_identifier_;
           code += "\"); }\n";
@@ -665,27 +709,24 @@
     code += "{ ";
     code += "__reset(_i, _bb); ";
     code += "}\n";
-    code +=
-        "  public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
+    code += "  public " + struct_class + " __assign(int _i, ByteBuffer _bb) ";
     code += "{ __init(_i, _bb); return this; }\n\n";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
       auto &field = **it;
       if (field.deprecated) continue;
-      GenComment(field.doc_comment, code_ptr, &comment_config, "  ");
-      std::string type_name = GenTypeGet(field.value.type);
-      std::string type_name_dest = GenTypeNameDest(field.value.type);
-      std::string conditional_cast = "";
-      std::string optional = "";
-      std::string dest_mask = DestinationMask(field.value.type, true);
-      std::string dest_cast = DestinationCast(field.value.type);
-      std::string src_cast = SourceCast(field.value.type);
-      std::string method_start =
+      GenComment(field.doc_comment, &code, &comment_config, "  ");
+      const std::string type_name = GenTypeGet(field.value.type);
+      const std::string type_name_dest = GenTypeNameDest(field.value.type);
+      const std::string dest_mask = DestinationMask(field.value.type, true);
+      const std::string dest_cast = DestinationCast(field.value.type);
+      const std::string src_cast = SourceCast(field.value.type);
+      const std::string method_start =
           "  public " +
           (field.IsRequired() ? "" : GenNullableAnnotation(field.value.type)) +
-          GenPureAnnotation(field.value.type) + type_name_dest + optional +
-          " " + ConvertCase(field.name, Case::kLowerCamel);
-      std::string obj = "obj";
+          GenPureAnnotation(field.value.type) + type_name_dest + " " +
+          namer_.Field(field);
+      const std::string obj = "obj";
 
       // Most field accessors need to retrieve and test the field offset first,
       // this is the prefix code for that:
@@ -698,7 +739,7 @@
       if (field.value.type.base_type == BASE_TYPE_STRUCT) {
         // Calls the accessor that takes an accessor object with a new object.
         code += method_start + "() { return ";
-        code += ConvertCase(field.name, Case::kLowerCamel);
+        code += namer_.Field(field);
         code += "(new ";
         code += type_name + "()); }\n";
       } else if (IsSeries(field.value.type) &&
@@ -706,14 +747,13 @@
         // Accessors for vectors of structs also take accessor objects, this
         // generates a variant without that argument.
         code += method_start + "(int j) { return ";
-        code += ConvertCase(field.name, Case::kLowerCamel);
+        code += namer_.Field(field);
         code += "(new " + type_name + "(), j); }\n";
       }
 
       if (field.IsScalarOptional()) { code += GenOptionalScalarCheck(field); }
       std::string getter = dest_cast + GenGetter(field.value.type);
       code += method_start;
-      std::string default_cast = "";
       std::string member_suffix = "; ";
       if (IsScalar(field.value.type.base_type)) {
         code += "()";
@@ -726,7 +766,7 @@
         } else {
           code += offset_prefix + getter;
           code += "(o + bb_pos)" + dest_mask;
-          code += " : " + default_cast;
+          code += " : ";
           code += GenDefaultValue(field);
         }
       } else {
@@ -738,7 +778,7 @@
               code += "bb_pos + " + NumToString(field.value.offset) + ", ";
               code += "bb)";
             } else {
-              code += offset_prefix + conditional_cast;
+              code += offset_prefix;
               code += obj + ".__assign(";
               code += field.value.type.struct_def->fixed
                           ? "o + bb_pos"
@@ -763,7 +803,7 @@
               code += type_name + " obj, ";
             }
             code += "int j)";
-            const auto body = offset_prefix + conditional_cast + getter + "(";
+            const auto body = offset_prefix + getter + "(";
             if (vectortype.base_type == BASE_TYPE_UNION) {
               code += body + "obj, ";
             } else {
@@ -787,11 +827,9 @@
             code += ")" + dest_mask;
             if (!IsArray(field.value.type)) {
               code += " : ";
-              code +=
-                  field.value.type.element == BASE_TYPE_BOOL
-                      ? "false"
-                      : (IsScalar(field.value.type.element) ? default_cast + "0"
-                                                            : "null");
+              code += field.value.type.element == BASE_TYPE_BOOL
+                          ? "false"
+                          : (IsScalar(field.value.type.element) ? "0" : "null");
             }
 
             break;
@@ -806,7 +844,7 @@
       code += member_suffix;
       code += "}\n";
       if (IsVector(field.value.type)) {
-        code += "  public int " + ConvertCase(field.name, Case::kLowerCamel);
+        code += "  public int " + namer_.Field(field);
         code += "Length";
         code += "()";
         code += offset_prefix;
@@ -821,9 +859,9 @@
           for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
             auto &key_field = **kit;
             if (key_field.key) {
-              auto qualified_name = WrapInNameSpace(sd);
+              auto qualified_name = namer_.NamespacedType(sd);
               code += "  public " + qualified_name + " ";
-              code += ConvertCase(field.name, Case::kLowerCamel) + "ByKey(";
+              code += namer_.Method(field) + "ByKey(";
               code += GenTypeNameDest(key_field.value.type) + " key)";
               code += offset_prefix;
               code += qualified_name + ".__lookup_by_key(";
@@ -832,7 +870,7 @@
               code += "bb) : null; ";
               code += "}\n";
               code += "  public " + qualified_name + " ";
-              code += ConvertCase(field.name, Case::kLowerCamel) + "ByKey(";
+              code += namer_.Method(field) + "ByKey(";
               code += qualified_name + " obj, ";
               code += GenTypeNameDest(key_field.value.type) + " key)";
               code += offset_prefix;
@@ -859,15 +897,14 @@
         } else {
           vector_type_name = type_name + ".Vector";
         }
-        auto vector_method_start =
-            GenNullableAnnotation(field.value.type) + "  public " +
-            vector_type_name + optional + " " +
-            ConvertCase(field.name, Case::kLowerCamel) + "Vector";
+        auto vector_method_start = GenNullableAnnotation(field.value.type) +
+                                   "  public " + vector_type_name + " " +
+                                   namer_.Field(field, "vector");
         code += vector_method_start + "() { return ";
-        code += ConvertCase(field.name, Case::kLowerCamel) + "Vector";
+        code += namer_.Field(field, "vector");
         code += "(new " + vector_type_name + "()); }\n";
         code += vector_method_start + "(" + vector_type_name + " obj)";
-        code += offset_prefix + conditional_cast + obj + ".__assign(";
+        code += offset_prefix + obj + ".__assign(";
         code += "__vector(o), ";
         if (!IsScalar(element_base_type)) {
           auto vectortype = field.value.type.VectorType();
@@ -880,7 +917,7 @@
            IsScalar(field.value.type.VectorType().base_type)) ||
           IsString(field.value.type)) {
         code += "  public ByteBuffer ";
-        code += ConvertCase(field.name, Case::kLowerCamel);
+        code += namer_.Field(field);
         code += "AsByteBuffer() { return ";
         code += "__vector_as_bytebuffer(";
         code += NumToString(field.value.offset) + ", ";
@@ -889,7 +926,7 @@
                                 : InlineSize(field.value.type.VectorType()));
         code += "); }\n";
         code += "  public ByteBuffer ";
-        code += ConvertCase(field.name, Case::kLowerCamel);
+        code += namer_.Field(field);
         code += "InByteBuffer(ByteBuffer _bb) { return ";
         code += "__vector_in_bytebuffer(_bb, ";
         code += NumToString(field.value.offset) + ", ";
@@ -900,9 +937,9 @@
       }
       // generate object accessors if is nested_flatbuffer
       if (field.nested_flatbuffer) {
-        auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
-        auto nested_method_name = ConvertCase(field.name, Case::kLowerCamel) +
-                                  "As" + field.nested_flatbuffer->name;
+        auto nested_type_name = namer_.NamespacedType(*field.nested_flatbuffer);
+        auto nested_method_name =
+            namer_.Field(field) + "As" + field.nested_flatbuffer->name;
         auto get_nested_method_name = nested_method_name;
         code += "  public " + nested_type_name + " ";
         code += nested_method_name + "() { return ";
@@ -913,7 +950,7 @@
         code += nested_type_name + " obj";
         code += ") { int o = __offset(";
         code += NumToString(field.value.offset) + "); ";
-        code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
+        code += "return o != 0 ? " + obj + ".__assign(";
         code += "";
         code += "__indirect(__vector(o)), ";
         code += "bb) : null; }\n";
@@ -928,7 +965,6 @@
         auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
                                     ? "(byte)(" + field.name + " ? 1 : 0)"
                                     : field.name;
-        auto mutator_prefix = "mutate";
         // A vector mutator also needs the index of the vector element it should
         // mutate.
         auto mutator_params = (is_series ? "(int j, " : "(") +
@@ -946,7 +982,7 @@
         if (IsScalar(underlying_type.base_type) && !IsUnion(field.value.type)) {
           code += "  public ";
           code += struct_def.fixed ? "void " : "boolean ";
-          code += mutator_prefix + ConvertCase(field.name, Case::kUpperCamel);
+          code += namer_.Method("mutate", field);
           code += mutator_params;
           if (struct_def.fixed) {
             code += GenSetter(underlying_type) + "(" + setter_index + ", ";
@@ -962,10 +998,9 @@
       }
       if (parser_.opts.java_primitive_has_method &&
           IsScalar(field.value.type.base_type) && !struct_def.fixed) {
-        auto vt_offset_constant =
-            "  public static final int VT_" +
-            ConvertCase(field.name, Case::kScreamingSnake) + " = " +
-            NumToString(field.value.offset) + ";";
+        auto vt_offset_constant = "  public static final int VT_" +
+                                  namer_.Constant(field) + " = " +
+                                  NumToString(field.value.offset) + ";";
 
         code += vt_offset_constant;
         code += "\n";
@@ -980,10 +1015,10 @@
       // create a struct constructor function
       code += "  public static " + GenOffsetType() + " ";
       code += "create";
-      code += struct_def.name + "(FlatBufferBuilder builder";
-      GenStructArgs(struct_def, code_ptr, "");
+      code += struct_class + "(FlatBufferBuilder builder";
+      GenStructArgs(struct_def, code, "");
       code += ") {\n";
-      GenStructBody(struct_def, code_ptr, "");
+      GenStructBody(struct_def, code, "");
       code += "    return ";
       code += GenOffsetConstruct("builder." + std::string("offset()"));
       code += ";\n  }\n";
@@ -1010,12 +1045,12 @@
         // Generate a table constructor of the form:
         // public static int createName(FlatBufferBuilder builder, args...)
         code += "  public static " + GenOffsetType() + " ";
-        code += "create" + struct_def.name;
+        code += namer_.LegacyJavaMethod2("create", struct_def, "");
         code += "(FlatBufferBuilder builder";
         for (auto it = struct_def.fields.vec.begin();
              it != struct_def.fields.vec.end(); ++it) {
           auto &field = **it;
-          auto field_name = ConvertCase(field.name, Case::kLowerCamel);
+          auto field_name = namer_.Field(field);
           if (field.deprecated) continue;
           code += ",\n      ";
           code += GenTypeBasic(DestinationType(field.value.type, false));
@@ -1031,21 +1066,19 @@
           for (auto it = struct_def.fields.vec.rbegin();
                it != struct_def.fields.vec.rend(); ++it) {
             auto &field = **it;
-            auto field_name = ConvertCase(field.name, Case::kLowerCamel);
-            auto method_name = ConvertCase(field.name, Case::kUpperCamel);
+            auto field_name = namer_.Field(field);
             if (!field.deprecated &&
                 (!struct_def.sortbysize ||
                  size == SizeOf(field.value.type.base_type))) {
-              code += "    " + struct_def.name + ".";
-              code += "add";
-              code += method_name + "(builder, " + field_name;
+              code += "    " + struct_class + ".";
+              code += namer_.Method("add", field) + "(builder, " + field_name;
               if (!IsScalar(field.value.type.base_type)) code += "Offset";
               code += ");\n";
             }
           }
         }
-        code += "    return " + struct_def.name + ".";
-        code += "end" + struct_def.name;
+        code += "    return " + struct_class + ".";
+        code += namer_.LegacyJavaMethod2("end", struct_def, "");
         code += "(builder);\n  }\n\n";
       }
       // Generate a set of static methods that allow table construction,
@@ -1054,7 +1087,7 @@
       // { builder.addShort(id, name, default); }
       // Unlike the Create function, these always work.
       code += "  public static void start";
-      code += struct_def.name;
+      code += struct_class;
       code += "(FlatBufferBuilder builder) { builder.";
       code += "startTable(";
       code += NumToString(struct_def.fields.vec.size()) + "); }\n";
@@ -1062,22 +1095,34 @@
            it != struct_def.fields.vec.end(); ++it) {
         auto &field = **it;
         if (field.deprecated) continue;
-        if (field.key) key_field = &field;
-        code += "  public static void add";
-        code += ConvertCase(field.name, Case::kUpperCamel);
+
+        code += "  public static void " + namer_.Method("add", field);
         code += "(FlatBufferBuilder builder, ";
         code += GenTypeBasic(DestinationType(field.value.type, false));
-        auto argname = ConvertCase(field.name, Case::kLowerCamel);
+        auto argname = namer_.Field(field);
         if (!IsScalar(field.value.type.base_type)) argname += "Offset";
         code += " " + argname + ") { builder.add";
         code += GenMethod(field.value.type) + "(";
-        code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
-        code += SourceCastBasic(field.value.type);
-        code += argname;
-        code += ", ";
-        code += SourceCastBasic(field.value.type);
-        code += GenDefaultValue(field);
-        code += "); }\n";
+
+        if (field.key) {
+          // field has key attribute, so always need to exist
+          // even if its value is equal to default.
+          // Generated code will bypass default checking
+          // resulting in { builder.addShort(name); slot(id); }
+          key_field = &field;
+          code += SourceCastBasic(field.value.type);
+          code += argname;
+          code += "); builder.slot(" +
+                  NumToString(it - struct_def.fields.vec.begin()) + "); }\n";
+        } else {
+          code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
+          code += SourceCastBasic(field.value.type);
+          code += argname;
+          code += ", ";
+          code += SourceCastBasic(field.value.type);
+          code += GenDefaultValue(field);
+          code += "); }\n";
+        }
         if (IsVector(field.value.type)) {
           auto vector_type = field.value.type.VectorType();
           auto alignment = InlineAlignment(vector_type);
@@ -1089,20 +1134,17 @@
                  vector_type.base_type == BASE_TYPE_UCHAR)) {
               // Handle byte[] and ByteBuffers separately for Java
               code += "  public static " + GenVectorOffsetType() + " ";
-              code += "create";
-              code += ConvertCase(field.name, Case::kUpperCamel);
+              code += namer_.Method("create", field);
               code += "Vector(FlatBufferBuilder builder, byte[] data) ";
               code += "{ return builder.createByteVector(data); }\n";
 
               code += "  public static " + GenVectorOffsetType() + " ";
-              code += "create";
-              code += ConvertCase(field.name, Case::kUpperCamel);
+              code += namer_.Method("create", field);
               code += "Vector(FlatBufferBuilder builder, ByteBuffer data) ";
               code += "{ return builder.createByteVector(data); }\n";
             } else {
               code += "  public static " + GenVectorOffsetType() + " ";
-              code += "create";
-              code += ConvertCase(field.name, Case::kUpperCamel);
+              code += namer_.Method("create", field);
               code += "Vector(FlatBufferBuilder builder, ";
               code += GenTypeBasic(DestinationType(vector_type, false)) +
                       "[] data) ";
@@ -1123,8 +1165,7 @@
           }
           // Generate a method to start a vector, data to be added manually
           // after.
-          code += "  public static void start";
-          code += ConvertCase(field.name, Case::kUpperCamel);
+          code += "  public static void " + namer_.Method("start", field);
           code += "Vector(FlatBufferBuilder builder, int numElems) ";
           code += "{ builder.startVector(";
           code += NumToString(elem_size);
@@ -1133,7 +1174,7 @@
         }
       }
       code += "  public static " + GenOffsetType() + " ";
-      code += "end" + struct_def.name;
+      code += namer_.LegacyJavaMethod2("end", struct_def, "");
       code += "(FlatBufferBuilder builder) {\n    int o = builder.";
       code += "endTable();\n";
       for (auto it = struct_def.fields.vec.begin();
@@ -1150,8 +1191,9 @@
         std::string size_prefix[] = { "", "SizePrefixed" };
         for (int i = 0; i < 2; ++i) {
           code += "  public static void ";
-          code += "finish" + size_prefix[i] + struct_def.name;
-          code += "Buffer(FlatBufferBuilder builder, " + GenOffsetType();
+          code += namer_.LegacyJavaMethod2("finish" + size_prefix[i],
+                                           struct_def, "Buffer");
+          code += "(FlatBufferBuilder builder, " + GenOffsetType();
           code += " offset) {";
           code += " builder.finish" + size_prefix[i] + "(offset";
 
@@ -1170,9 +1212,9 @@
       code += GenKeyGetter(key_field);
       code += " }\n";
 
-      code += "\n  public static " + struct_def.name;
+      code += "\n  public static " + struct_class;
       code += " __lookup_by_key(";
-      code += struct_def.name + " obj, ";
+      code += struct_class + " obj, ";
       code += "int vectorLocation, ";
       code += GenTypeNameDest(key_field->value.type);
       code += " key, ByteBuffer bb) {\n";
@@ -1194,15 +1236,15 @@
       code += "        span -= middle;\n";
       code += "      } else {\n";
       code += "        return ";
-      code += "(obj == null ? new " + struct_def.name + "() : obj)";
+      code += "(obj == null ? new " + struct_class + "() : obj)";
       code += ".__assign(tableOffset, bb);\n";
       code += "      }\n    }\n";
       code += "    return null;\n";
       code += "  }\n";
     }
-    GenVectorAccessObject(struct_def, code_ptr);
+    GenVectorAccessObject(struct_def, code);
     if (opts.generate_object_based_api) {
-      GenPackUnPack_ObjectAPI(struct_def, code_ptr, opts, struct_has_create,
+      GenPackUnPack_ObjectAPI(struct_def, code, opts, struct_has_create,
                               field_has_create_set);
     }
     code += "}\n\n";
@@ -1210,14 +1252,12 @@
 
   std::string GenOptionalScalarCheck(FieldDef &field) const {
     if (!field.IsScalarOptional()) return "";
-    return "  public boolean has" + ConvertCase(field.name, Case::kUpperCamel) +
+    return "  public boolean " + namer_.Method("has", field) +
            "() { return 0 != __offset(" + NumToString(field.value.offset) +
            "); }\n";
   }
 
-  void GenVectorAccessObject(StructDef &struct_def,
-                             std::string *code_ptr) const {
-    auto &code = *code_ptr;
+  void GenVectorAccessObject(StructDef &struct_def, std::string &code) const {
     // Generate a vector of structs accessor class.
     code += "\n";
     code += "  ";
@@ -1234,7 +1274,7 @@
     code += "__assign(int _vector, int _element_size, ByteBuffer _bb) { ";
     code += "__reset(_vector, _element_size, _bb); return this; }\n\n";
 
-    auto type_name = struct_def.name;
+    auto type_name = namer_.Type(struct_def);
     auto method_start = method_indent + "public " + type_name + " get";
     // Generate the accessors that don't do object reuse.
     code += method_start + "(int j) { return get";
@@ -1276,18 +1316,12 @@
     code += "  }\n";
   }
 
-  std::string GenGetterFuncName_ObjectAPI(const std::string &field_name) const {
-    return "get" + ConvertCase(field_name, Case::kUpperCamel);
-  }
-
-  void GenEnum_ObjectAPI(EnumDef &enum_def, std::string *code_ptr,
-                         const IDLOptions &opts) const {
-    auto &code = *code_ptr;
+  void GenEnum_ObjectAPI(EnumDef &enum_def, std::string &code) const {
     if (enum_def.generated) return;
     code += "import com.google.flatbuffers.FlatBufferBuilder;\n\n";
 
     if (!enum_def.attributes.Lookup("private")) { code += "public "; }
-    auto union_name = enum_def.name + "Union";
+    auto union_name = namer_.Type(enum_def) + "Union";
     auto union_type =
         GenTypeBasic(DestinationType(enum_def.underlying_type, false));
     code += "class " + union_name + " {\n";
@@ -1304,15 +1338,16 @@
     code += "  public void setValue(Object value) { this.value = value; }\n\n";
     // Constructor
     code += "  public " + union_name + "() {\n";
-    code += "    this.type = " + enum_def.name + "." +
-            enum_def.Vals()[0]->name + ";\n";
+    code +=
+        "    this.type = " + namer_.EnumVariant(enum_def, *enum_def.Vals()[0]) +
+        ";\n";
     code += "    this.value = null;\n";
     code += "  }\n\n";
     // As
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       auto &ev = **it;
       if (ev.union_type.base_type == BASE_TYPE_NONE) continue;
-      auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts, false, true);
+      auto type_name = GenTypeGet_ObjectAPI(ev.union_type, false, true);
       if (ev.union_type.base_type == BASE_TYPE_STRUCT &&
           ev.union_type.struct_def->attributes.Lookup("private")) {
         code += "  ";
@@ -1332,7 +1367,7 @@
       if (ev.union_type.base_type == BASE_TYPE_NONE) {
         continue;
       } else {
-        code += "      case " + enum_def.name + "." + ev.name + ": return ";
+        code += "      case " + namer_.EnumVariant(enum_def, ev) + ": return ";
         if (IsString(ev.union_type)) {
           code += "builder.createString(_o.as" + ev.name + "());\n";
         } else {
@@ -1347,41 +1382,23 @@
     code += "}\n\n";
   }
 
-  std::string GenSetterFuncName_ObjectAPI(const std::string &field_name) const {
-    return "set" + ConvertCase(field_name, Case::kUpperCamel);
-  }
-
-  std::string GenTypeName_ObjectAPI(const std::string &name,
-                                    const IDLOptions &opts) const {
-    return opts.object_prefix + name + opts.object_suffix;
-  }
-
-  void GenUnionUnPack_ObjectAPI(const EnumDef &enum_def, std::string *code_ptr,
+  void GenUnionUnPack_ObjectAPI(const EnumDef &enum_def, std::string &code,
                                 const std::string &type_name,
-                                const std::string &camel_name,
+                                const std::string &field_name,
                                 bool is_vector) const {
-    auto &code = *code_ptr;
+    const std::string variable_type =
+        is_vector ? type_name.substr(0, type_name.length() - 2) : type_name;
+    const std::string variable_name =
+        "_" + namer_.Variable("o", field_name) + (is_vector ? "Element" : "");
+    const std::string type_params = is_vector ? "_j" : "";
+    const std::string value_params = is_vector ? ", _j" : "";
+    const std::string indent = (is_vector ? "      " : "    ");
 
-    std::string variable_type = type_name;
-    std::string variable_name =
-        "_o" + ConvertCase(camel_name, Case::kUpperCamel);
-    std::string type_params = "";
-    std::string value_params = "";
-    std::string func_suffix = "()";
-    std::string indent = "    ";
-    if (is_vector) {
-      variable_type = type_name.substr(0, type_name.length() - 2);
-      variable_name += "Element";
-      type_params = "_j";
-      value_params = ", _j";
-      func_suffix = "(_j)";
-      indent = "      ";
-    }
     code += indent + variable_type + " " + variable_name + " = new " +
             variable_type + "();\n";
     code += indent +
             GenTypeBasic(DestinationType(enum_def.underlying_type, false)) +
-            " " + variable_name + "Type = " + camel_name + "Type(" +
+            " " + variable_name + "Type = " + field_name + "Type(" +
             type_params + ");\n";
     code += indent + variable_name + ".setType(" + variable_name + "Type);\n";
     code += indent + "Table " + variable_name + "Value;\n";
@@ -1399,10 +1416,10 @@
                      // Java which doesn't handle non Table types. Should be
                      // deleted when issue #6561 is fixed.
         }
-        code += indent + "  case " + WrapInNameSpace(enum_def) + "." + ev.name +
-                ":\n";
+        code += indent + "  case " +
+                namer_.NamespacedEnumVariant(enum_def, ev) + ":\n";
         auto actual_type = GenTypeGet(ev.union_type);
-        code += indent + "    " + variable_name + "Value = " + camel_name +
+        code += indent + "    " + variable_name + "Value = " + field_name +
                 "(new " + actual_type + "()" + value_params + ");\n";
         code += indent + "    " + variable_name + ".setValue(" + variable_name +
                 "Value != null ? ((" + actual_type + ") " + variable_name +
@@ -1413,17 +1430,16 @@
     code += indent + "  default: break;\n";
     code += indent + "}\n";
     if (is_vector) {
-      code += indent + "_o" + ConvertCase(camel_name, Case::kUpperCamel) +
+      code += indent + "_" + namer_.Variable("o", field_name) +
               "[_j] = " + variable_name + ";\n";
     }
   }
 
   void GenPackUnPack_ObjectAPI(
-      StructDef &struct_def, std::string *code_ptr, const IDLOptions &opts,
+      StructDef &struct_def, std::string &code, const IDLOptions &opts,
       bool struct_has_create,
       const std::set<FieldDef *> &field_has_create) const {
-    auto &code = *code_ptr;
-    auto struct_name = GenTypeName_ObjectAPI(struct_def.name, opts);
+    auto struct_name = namer_.ObjectType(struct_def);
     // unpack()
     code += "  public " + struct_name + " unpack() {\n";
     code += "    " + struct_name + " _o = new " + struct_name + "();\n";
@@ -1434,35 +1450,34 @@
     code += "  public void unpackTo(" + struct_name + " _o) {\n";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
-      auto camel_name = ConvertCase(field.name, Case::kLowerCamel);
-      auto camel_name_with_first = ConvertCase(field.name, Case::kUpperCamel);
-      auto type_name =
-          GenTypeGet_ObjectAPI(field.value.type, opts, false, true);
+      const auto accessor = namer_.Method(field);
+      const auto variable = "_" + namer_.Variable("o", field);
+      const auto get_field = namer_.Method("get", field);
+      const auto set_field = namer_.Method("set", field);
+
+      auto type_name = GenTypeGet_ObjectAPI(field.value.type, false, true);
       if (field.IsScalarOptional())
         type_name = ConvertPrimitiveTypeToObjectWrapper_ObjectAPI(type_name);
-      auto start = "    " + type_name + " _o" + camel_name_with_first + " = ";
+      auto start = "    " + type_name + " " + variable + " = ";
       auto call_setter = true;
       switch (field.value.type.base_type) {
         case BASE_TYPE_STRUCT: {
           auto fixed = struct_def.fixed && field.value.type.struct_def->fixed;
           if (fixed) {
-            code += "    " + camel_name + "().unpackTo(_o.get" +
-                    camel_name_with_first + "());\n";
+            code +=
+                "    " + accessor + "().unpackTo(_o." + get_field + "());\n";
           } else {
-            code += "    if (" + camel_name + "() != null) ";
+            code += "    if (" + accessor + "() != null) ";
             if (field.value.type.struct_def->fixed) {
-              code += camel_name + "().unpackTo(_o.get" +
-                      camel_name_with_first + "());\n";
+              code += accessor + "().unpackTo(_o." + get_field + "());\n";
             } else {
-              code += "_o." + GenSetterFuncName_ObjectAPI(field.name) + "(" +
-                      camel_name + "().unpack());\n";
+              code += "_o." + set_field + "(" + accessor + "().unpack());\n";
             }
-            code += "    else _o." + GenSetterFuncName_ObjectAPI(field.name) +
-                    "(null);\n";
+            code += "    else _o." + set_field + "(null);\n";
           }
           call_setter = false;
           break;
@@ -1471,38 +1486,38 @@
           auto length_str = NumToString(field.value.type.fixed_length);
           auto unpack_method =
               field.value.type.struct_def == nullptr ? "" : ".unpack()";
-          code +=
-              start + "_o." + GenGetterFuncName_ObjectAPI(field.name) + "();\n";
-          code += "    for (int _j = 0; _j < " + length_str + "; ++_j) { _o" +
-                  camel_name_with_first + "[_j] = " + camel_name + "(_j)" +
-                  unpack_method + "; }\n";
+          code += start + "_o." + get_field + "();\n";
+          code += "    for (int _j = 0; _j < " + length_str + "; ++_j) { " +
+                  variable + "[_j] = " + accessor + "(_j)" + unpack_method +
+                  "; }\n";
           call_setter = false;
           break;
         }
         case BASE_TYPE_VECTOR:
           if (field.value.type.element == BASE_TYPE_UNION) {
             code += start + "new " +
-                    GenConcreteTypeGet_ObjectAPI(field.value.type, opts)
+                    GenConcreteTypeGet_ObjectAPI(field.value.type)
                         .substr(0, type_name.length() - 1) +
-                    camel_name + "Length()];\n";
-            code += "    for (int _j = 0; _j < " + camel_name +
-                    "Length(); ++_j) {\n";
-            GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
-                                     type_name, camel_name, true);
+                    accessor + "Length()];\n";
+            code +=
+                "    for (int _j = 0; _j < " + accessor + "Length(); ++_j) {\n";
+            GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code,
+                                     type_name, accessor, true);
             code += "    }\n";
           } else if (field.value.type.element != BASE_TYPE_UTYPE) {
             auto fixed = field.value.type.struct_def == nullptr;
+            const auto length_accessor = namer_.Method(field, "length");
             code += start + "new " +
-                    GenConcreteTypeGet_ObjectAPI(field.value.type, opts)
+                    GenConcreteTypeGet_ObjectAPI(field.value.type)
                         .substr(0, type_name.length() - 1) +
-                    camel_name + "Length()];\n";
+                    length_accessor + "()];\n";
             code +=
-                "    for (int _j = 0; _j < " + camel_name + "Length(); ++_j) {";
-            code += "_o" + camel_name_with_first + "[_j] = ";
+                "    for (int _j = 0; _j < " + length_accessor + "(); ++_j) {";
+            code += variable + "[_j] = ";
             if (fixed) {
-              code += camel_name + "(_j)";
+              code += accessor + "(_j)";
             } else {
-              code += "(" + camel_name + "(_j) != null ? " + camel_name +
+              code += "(" + accessor + "(_j) != null ? " + accessor +
                       "(_j).unpack() : null)";
             }
             code += ";}\n";
@@ -1510,23 +1525,22 @@
           break;
         case BASE_TYPE_UTYPE: break;
         case BASE_TYPE_UNION: {
-          GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
-                                   type_name, camel_name, false);
+          GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code, type_name,
+                                   accessor, false);
           break;
         }
         default: {
           if (field.IsScalarOptional()) {
-            code += start + "has" + camel_name_with_first + "() ? " +
-                    camel_name + "() : null;\n";
+            code += start + namer_.Method("has", field) + "() ? " + accessor +
+                    "() : null;\n";
           } else {
-            code += start + camel_name + "();\n";
+            code += start + accessor + "();\n";
           }
           break;
         }
       }
       if (call_setter) {
-        code += "    _o." + GenSetterFuncName_ObjectAPI(field.name) + "(_o" +
-                camel_name_with_first + ");\n";
+        code += "    _o." + set_field + "(" + variable + ");\n";
       }
     }
     code += "  }\n";
@@ -1538,17 +1552,17 @@
          it != struct_def.fields.vec.end(); ++it) {
       auto &field = **it;
       if (field.deprecated) continue;
-      auto camel_name = ConvertCase(field.name, Case::kLowerCamel);
-      auto camel_name_with_first = ConvertCase(field.name, Case::kUpperCamel);
+      const auto field_name = namer_.Field(field);
+      const auto variable = "_" + namer_.Variable("o", field);
+      const auto get_field = namer_.Method("get", field);
       // pre
       switch (field.value.type.base_type) {
         case BASE_TYPE_STRUCT: {
           if (!field.value.type.struct_def->fixed) {
-            code += "    " + GenOffsetType() + " _" + field.name + " = _o." +
-                    GenGetterFuncName_ObjectAPI(field.name) +
+            code += "    " + GenOffsetType() + " _" + namer_.Variable(field) +
+                    " = _o." + get_field +
                     "() == null ? 0 : " + GenTypeGet(field.value.type) +
-                    ".pack(builder, _o." +
-                    GenGetterFuncName_ObjectAPI(field.name) + "());\n";
+                    ".pack(builder, _o." + get_field + "());\n";
           } else if (struct_def.fixed && struct_has_create) {
             std::vector<FieldArrayLength> array_lengths;
             FieldArrayLength tmp_array_length = {
@@ -1556,60 +1570,54 @@
               field.value.type.fixed_length,
             };
             array_lengths.push_back(tmp_array_length);
-            GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
-                                        array_lengths);
+            GenStructPackDecl_ObjectAPI(*field.value.type.struct_def,
+                                        array_lengths, code);
           }
           break;
         }
         case BASE_TYPE_STRING: {
-          std::string create_string = "createString";
-          code += "    int _" + camel_name + " = _o." +
-                  GenGetterFuncName_ObjectAPI(field.name) +
+          code += "    int _" + field_name + " = _o." + get_field +
                   "() == null ? 0 : "
-                  "builder." +
-                  create_string + "(_o." +
-                  GenGetterFuncName_ObjectAPI(field.name) + "());\n";
+                  "builder.createString(_o." +
+                  get_field + "());\n";
           break;
         }
         case BASE_TYPE_VECTOR: {
           if (field_has_create.find(&field) != field_has_create.end()) {
-            auto property_name = camel_name;
+            auto property_name = field_name;
             auto gen_for_loop = true;
-            std::string array_name = "__" + camel_name;
+            std::string array_name = "__" + field_name;
             std::string array_type = "";
             std::string element_type = "";
             std::string to_array = "";
             switch (field.value.type.element) {
               case BASE_TYPE_STRING: {
-                std::string create_string = "createString";
                 array_type = "int";
                 element_type = "String";
-                to_array += "builder." + create_string + "(_e)";
+                to_array = "builder.createString(_e)";
                 break;
               }
               case BASE_TYPE_STRUCT:
                 array_type = "int";
                 element_type =
-                    GenTypeGet_ObjectAPI(field.value.type, opts, true, true);
+                    GenTypeGet_ObjectAPI(field.value.type, true, true);
                 ;
                 to_array = GenTypeGet(field.value.type) + ".pack(builder, _e)";
                 break;
               case BASE_TYPE_UTYPE:
-                property_name = camel_name.substr(0, camel_name.size() - 4);
+                property_name = field_name.substr(0, field_name.size() - 4);
                 array_type = GenTypeBasic(DestinationType(
                     field.value.type.enum_def->underlying_type, false));
                 element_type = field.value.type.enum_def->name + "Union";
-                to_array = "_o." + GenGetterFuncName_ObjectAPI(property_name) +
+                to_array = "_o." + namer_.Method("get", property_name) +
                            "()[_j].getType()";
                 break;
               case BASE_TYPE_UNION:
                 array_type = "int";
                 element_type =
-                    WrapInNameSpace(*field.value.type.enum_def) + "Union";
-                to_array = WrapInNameSpace(*field.value.type.enum_def) +
-                           "Union.pack(builder,  _o." +
-                           GenGetterFuncName_ObjectAPI(property_name) +
-                           "()[_j])";
+                    namer_.NamespacedType(*field.value.type.enum_def) + "Union";
+                to_array = element_type + ".pack(builder,  _o." +
+                           namer_.Method("get", property_name) + "()[_j])";
                 break;
               case BASE_TYPE_UCHAR:  // TODO this branch of the switch is due to
                                      // inconsistent behavior in unsigned byte.
@@ -1620,52 +1628,51 @@
                 break;
               default:
                 gen_for_loop = false;
-                array_name =
-                    "_o." + GenGetterFuncName_ObjectAPI(property_name) + "()";
+                array_name = "_o." + namer_.Method("get", property_name) + "()";
                 array_type = GenTypeNameDest(field.value.type);
                 element_type = array_type;
                 to_array = "_e";
                 break;
             }
-            code += "    int _" + camel_name + " = 0;\n";
-            code += "    if (_o." + GenGetterFuncName_ObjectAPI(property_name) +
+            code += "    int _" + field_name + " = 0;\n";
+            code += "    if (_o." + namer_.Method("get", property_name) +
                     "() != null) {\n";
             if (gen_for_loop) {
               code += "      " + array_type + "[] " + array_name + " = new " +
                       array_type + "[_o." +
-                      GenGetterFuncName_ObjectAPI(property_name) +
-                      "().length];\n";
+                      namer_.Method("get", property_name) + "().length];\n";
               code += "      int _j = 0;\n";
               code += "      for (" + element_type + " _e : _o." +
-                      GenGetterFuncName_ObjectAPI(property_name) + "()) { ";
+                      namer_.Method("get", property_name) + "()) { ";
               code += array_name + "[_j] = " + to_array + "; _j++;}\n";
             }
-            code += "      _" + camel_name + " = create" +
-                    camel_name_with_first + "Vector(builder, " + array_name +
-                    ");\n";
+            code += "      _" + field_name + " = " +
+                    namer_.Method("create", field) + "Vector(builder, " +
+                    array_name + ");\n";
             code += "    }\n";
           } else {
             auto type_name = GenTypeGet(field.value.type);
             auto element_type_name =
-                GenTypeGet_ObjectAPI(field.value.type, opts, true, true);
+                GenTypeGet_ObjectAPI(field.value.type, true, true);
             auto pack_method =
                 field.value.type.struct_def == nullptr
                     ? "builder.add" + GenMethod(field.value.type.VectorType()) +
-                          "(_o" + camel_name_with_first + "[_j]);"
-                    : type_name + ".pack(builder, _o" + camel_name_with_first +
-                          "[_j]);";
-            code += "    int _" + camel_name + " = 0;\n";
-            code += "    " + element_type_name + "[] _o" +
-                    camel_name_with_first + " = _o." +
-                    GenGetterFuncName_ObjectAPI(field.name) + "();\n";
-            code += "    if (_o" + camel_name_with_first + " != null) {\n";
-            code += "      start" + camel_name_with_first +
-                    "Vector(builder, _o" + camel_name_with_first +
-                    ".length);\n";
-            code += "      for (int _j = _o" + camel_name_with_first +
+                          "(" + variable + "[_j]);"
+                    : "_unused_offset = " + type_name + ".pack(builder, " +
+                          variable + "[_j]);";
+            code += "    int _" + field_name + " = 0;\n";
+            code += "    " + element_type_name + "[] " + variable + " = _o." +
+                    get_field + "();\n";
+            code += "    if (" + variable + " != null) {\n";
+            if (field.value.type.struct_def != nullptr) {
+              code += "      int _unused_offset = 0;\n";
+            }
+            code += "      " + namer_.Method("start", field) +
+                    "Vector(builder, " + variable + ".length);\n";
+            code += "      for (int _j = " + variable +
                     ".length - 1; _j >=0; _j--) { ";
             code += pack_method + "}\n";
-            code += "      _" + camel_name + " = builder.endVector();\n";
+            code += "      _" + field_name + " = builder.endVector();\n";
             code += "    }\n";
           }
           break;
@@ -1678,29 +1685,27 @@
               field.value.type.fixed_length,
             };
             array_lengths.push_back(tmp_array_length);
-            GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
-                                        array_lengths);
+            GenStructPackDecl_ObjectAPI(*field.value.type.struct_def,
+                                        array_lengths, code);
           } else {
             code += "    " +
-                    GenTypeGet_ObjectAPI(field.value.type, opts, false, true) +
-                    " _" + camel_name + " = _o." +
-                    GenGetterFuncName_ObjectAPI(field.name) + "();\n";
+                    GenTypeGet_ObjectAPI(field.value.type, false, true) + " _" +
+                    field_name + " = _o." + get_field + "();\n";
           }
           break;
         }
         case BASE_TYPE_UNION: {
-          code +=
-              "    " +
-              GenTypeBasic(DestinationType(
-                  field.value.type.enum_def->underlying_type, false)) +
-              " _" + camel_name + "Type = _o.get" + camel_name_with_first +
-              "() == null ? " + WrapInNameSpace(*field.value.type.enum_def) +
-              ".NONE : " + "_o.get" + camel_name_with_first + "().getType();\n";
-          code += "    " + GenOffsetType() + " _" + camel_name + " = _o.get" +
-                  camel_name_with_first + "() == null ? 0 : " +
-                  WrapInNameSpace(*field.value.type.enum_def) +
-                  "Union.pack(builder, _o.get" + camel_name_with_first +
-                  "());\n";
+          code += "    " +
+                  GenTypeBasic(DestinationType(
+                      field.value.type.enum_def->underlying_type, false)) +
+                  " _" + field_name + "Type = _o." + get_field +
+                  "() == null ? " +
+                  namer_.NamespacedType(*field.value.type.enum_def) +
+                  ".NONE : " + "_o." + get_field + "().getType();\n";
+          code += "    " + GenOffsetType() + " _" + field_name + " = _o." +
+                  get_field + "() == null ? 0 : " +
+                  namer_.NamespacedType(*field.value.type.enum_def) +
+                  "Union.pack(builder, _o." + get_field + "());\n";
           break;
         }
         default: break;
@@ -1708,42 +1713,42 @@
     }
     if (struct_has_create) {
       // Create
-      code += "    return create" + struct_def.name + "(\n";
+      code += "    return " +
+              namer_.LegacyJavaMethod2("create", struct_def, "") + "(\n";
       code += "      builder";
       for (auto it = struct_def.fields.vec.begin();
            it != struct_def.fields.vec.end(); ++it) {
         auto &field = **it;
         if (field.deprecated) continue;
-        auto camel_name = ConvertCase(field.name, Case::kLowerCamel);
+        const auto field_name = namer_.Field(field);
+        const auto get_field = namer_.Method("get", field);
         switch (field.value.type.base_type) {
           case BASE_TYPE_STRUCT: {
             if (struct_def.fixed) {
-              GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
-                                          code_ptr,
-                                          "      _" + camel_name + "_");
+              GenStructPackCall_ObjectAPI(*field.value.type.struct_def, code,
+                                          "      _" + field_name + "_");
             } else {
               code += ",\n";
               if (field.value.type.struct_def->fixed) {
                 if (opts.generate_object_based_api)
-                  code += "      _o." + camel_name;
+                  code += "      _o." + field_name;
                 else
                   // Seems like unreachable code
                   code += "      " + GenTypeGet(field.value.type) +
-                          ".Pack(builder, _o." + camel_name + ")";
+                          ".Pack(builder, _o." + field_name + ")";
               } else {
-                code += "      _" + field.name;
+                code += "      _" + field_name;
               }
             }
             break;
           }
           case BASE_TYPE_ARRAY: {
             if (field.value.type.struct_def != nullptr) {
-              GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
-                                          code_ptr,
-                                          "      _" + camel_name + "_");
+              GenStructPackCall_ObjectAPI(*field.value.type.struct_def, code,
+                                          "      _" + field_name + "_");
             } else {
               code += ",\n";
-              code += "      _" + camel_name;
+              code += "      _" + field_name;
             }
             break;
           }
@@ -1752,82 +1757,78 @@
           case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH();  // fall thru
           case BASE_TYPE_VECTOR: {
             code += ",\n";
-            code += "      _" + camel_name;
+            code += "      _" + field_name;
             break;
           }
           default:  // scalar
             code += ",\n";
-            code +=
-                "      _o." + GenGetterFuncName_ObjectAPI(field.name) + "()";
+            code += "      _o." + get_field + "()";
             break;
         }
       }
       code += ");\n";
     } else {
       // Start, End
-      code += "    start" + struct_def.name + "(builder);\n";
+      code += "    " + namer_.LegacyJavaMethod2("start", struct_def, "") +
+              "(builder);\n";
       for (auto it = struct_def.fields.vec.begin();
            it != struct_def.fields.vec.end(); ++it) {
         auto &field = **it;
         if (field.deprecated) continue;
-        auto camel_name = ConvertCase(field.name, Case::kLowerCamel);
-        auto camel_name_with_first = ConvertCase(field.name, Case::kUpperCamel);
+        const auto arg = "_" + namer_.Variable(field);
+        const auto get_field = namer_.Method("get", field);
+        const auto add_field = namer_.Method("add", field);
+
         switch (field.value.type.base_type) {
           case BASE_TYPE_STRUCT: {
             if (field.value.type.struct_def->fixed) {
-              code += "    add" + camel_name_with_first + "(builder, " +
+              code += "    " + add_field + "(builder, " +
                       GenTypeGet(field.value.type) + ".pack(builder, _o." +
-                      GenGetterFuncName_ObjectAPI(field.name) + "()));\n";
+                      get_field + "()));\n";
             } else {
-              code += "    add" + camel_name_with_first + "(builder, _" +
-                      field.name + ");\n";
+              code += "    " + add_field + "(builder, " + arg + ");\n";
             }
             break;
           }
           case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH();  // fall thru
           case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();   // fall thru
           case BASE_TYPE_VECTOR: {
-            code += "    add" + camel_name_with_first + "(builder, _" +
-                    camel_name + ");\n";
+            code += "    " + add_field + "(builder, " + arg + ");\n";
             break;
           }
           case BASE_TYPE_UTYPE: break;
           case BASE_TYPE_UNION: {
-            code += "    add" + camel_name_with_first + "Type(builder, _" +
-                    camel_name + "Type);\n";
-            code += "    add" + camel_name_with_first + "(builder, _" +
-                    camel_name + ");\n";
+            code += "    " + add_field + "Type(builder, " + arg + "Type);\n";
+            code += "    " + add_field + "(builder, " + arg + ");\n";
             break;
           }
           // scalar
           default: {
             if (field.IsScalarOptional()) {
-              code += "    if (_o." + GenGetterFuncName_ObjectAPI(field.name) +
-                      "() != null) { add" + camel_name_with_first +
-                      "(builder, _o." +
-                      GenGetterFuncName_ObjectAPI(field.name) + "()); }\n";
+              code += "    if (_o." + get_field + "() != null) { " + add_field +
+                      "(builder, _o." + get_field + "()); }\n";
             } else {
-              code += "    add" + camel_name_with_first + "(builder, _o." +
-                      GenGetterFuncName_ObjectAPI(field.name) + "());\n";
+              code +=
+                  "    " + add_field + "(builder, _o." + get_field + "());\n";
             }
             break;
           }
         }
       }
-      code += "    return end" + struct_def.name + "(builder);\n";
+      code += "    return " + namer_.LegacyJavaMethod2("end", struct_def, "") +
+              "(builder);\n";
     }
     code += "  }\n";
   }
 
-  void GenStructPackDecl_ObjectAPI(
-      const StructDef &struct_def, std::string *code_ptr,
-      std::vector<FieldArrayLength> &array_lengths) const {
-    auto &code = *code_ptr;
+  void GenStructPackDecl_ObjectAPI(const StructDef &struct_def,
+                                   std::vector<FieldArrayLength> &array_lengths,
+                                   std::string &code) const {
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
-      auto is_array = IsArray(field.value.type);
-      const auto &field_type =
+      const FieldDef &field = **it;
+      const bool is_array = IsArray(field.value.type);
+      const Type &field_type =
           is_array ? field.value.type.VectorType() : field.value.type;
       FieldArrayLength tmp_array_length = {
         field.name,
@@ -1835,8 +1836,8 @@
       };
       array_lengths.push_back(tmp_array_length);
       if (field_type.struct_def != nullptr) {
-        GenStructPackDecl_ObjectAPI(*field_type.struct_def, code_ptr,
-                                    array_lengths);
+        GenStructPackDecl_ObjectAPI(*field_type.struct_def, array_lengths,
+                                    code);
       } else {
         std::vector<FieldArrayLength> array_only_lengths;
         for (size_t i = 0; i < array_lengths.size(); ++i) {
@@ -1846,7 +1847,7 @@
         }
         std::string name;
         for (size_t i = 0; i < array_lengths.size(); ++i) {
-          name += "_" + ConvertCase(array_lengths[i].name, Case::kLowerCamel);
+          name += "_" + namer_.Variable(array_lengths[i].name);
         }
         code += "    " + GenTypeBasic(field_type);
         if (array_only_lengths.size() > 0) {
@@ -1878,8 +1879,7 @@
           }
           code += "] = _o";
           for (size_t i = 0, j = 0; i < array_lengths.size(); ++i) {
-            code +=
-                "." + GenGetterFuncName_ObjectAPI(array_lengths[i].name) + "()";
+            code += "." + namer_.Method("get", array_lengths[i].name) + "()";
             if (array_lengths[i].length <= 0) continue;
             code += "[idx" + NumToString(j++) + "]";
           }
@@ -1891,8 +1891,7 @@
           code += " " + name + " = ";
           code += "_o";
           for (size_t i = 0; i < array_lengths.size(); ++i) {
-            code +=
-                "." + GenGetterFuncName_ObjectAPI(array_lengths[i].name) + "()";
+            code += "." + namer_.Method("get", array_lengths[i].name) + "()";
           }
           code += ";";
         }
@@ -1903,26 +1902,24 @@
   }
 
   void GenStructPackCall_ObjectAPI(const StructDef &struct_def,
-                                   std::string *code_ptr,
+                                   std::string &code,
                                    std::string prefix) const {
-    auto &code = *code_ptr;
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
       auto &field = **it;
       const auto &field_type = field.value.type;
       if (field_type.struct_def != nullptr) {
-        GenStructPackCall_ObjectAPI(
-            *field_type.struct_def, code_ptr,
-            prefix + ConvertCase(field.name, Case::kLowerCamel) + "_");
+        GenStructPackCall_ObjectAPI(*field_type.struct_def, code,
+                                    prefix + namer_.Field(field) + "_");
       } else {
         code += ",\n";
-        code += prefix + ConvertCase(field.name, Case::kLowerCamel);
+        code += prefix + namer_.Field(field);
       }
     }
   }
 
   std::string ConvertPrimitiveTypeToObjectWrapper_ObjectAPI(
-      std::string type_name) const {
+      const std::string &type_name) const {
     if (type_name == "boolean")
       return "Boolean";
     else if (type_name == "byte")
@@ -1942,8 +1939,8 @@
     return type_name;
   }
 
-  std::string GenTypeGet_ObjectAPI(flatbuffers::Type type,
-                                   const IDLOptions &opts, bool vectorelem,
+  std::string GenTypeGet_ObjectAPI(const flatbuffers::Type &type,
+                                   bool vectorelem,
                                    bool wrap_in_namespace) const {
     auto type_name = GenTypeNameDest(type);
     // Replace to ObjectBaseAPI Type Name
@@ -1953,15 +1950,14 @@
       case BASE_TYPE_VECTOR: {
         if (type.struct_def != nullptr) {
           auto type_name_length = type.struct_def->name.length();
-          auto new_type_name =
-              GenTypeName_ObjectAPI(type.struct_def->name, opts);
+          auto new_type_name = namer_.ObjectType(*type.struct_def);
           type_name.replace(type_name.length() - type_name_length,
                             type_name_length, new_type_name);
         } else if (type.element == BASE_TYPE_UNION) {
           if (wrap_in_namespace) {
-            type_name = WrapInNameSpace(*type.enum_def) + "Union";
+            type_name = namer_.NamespacedType(*type.enum_def) + "Union";
           } else {
-            type_name = type.enum_def->name + "Union";
+            type_name = namer_.Type(*type.enum_def) + "Union";
           }
         }
         break;
@@ -1969,9 +1965,9 @@
 
       case BASE_TYPE_UNION: {
         if (wrap_in_namespace) {
-          type_name = WrapInNameSpace(*type.enum_def) + "Union";
+          type_name = namer_.NamespacedType(*type.enum_def) + "Union";
         } else {
-          type_name = type.enum_def->name + "Union";
+          type_name = namer_.Type(*type.enum_def) + "Union";
         }
         break;
       }
@@ -1989,8 +1985,8 @@
     return type_name;
   }
 
-  std::string GenConcreteTypeGet_ObjectAPI(flatbuffers::Type type,
-                                           const IDLOptions &opts) const {
+  std::string GenConcreteTypeGet_ObjectAPI(
+      const flatbuffers::Type &type) const {
     auto type_name = GenTypeNameDest(type);
     // Replace to ObjectBaseAPI Type Name
     switch (type.base_type) {
@@ -1999,18 +1995,17 @@
       case BASE_TYPE_VECTOR: {
         if (type.struct_def != nullptr) {
           auto type_name_length = type.struct_def->name.length();
-          auto new_type_name =
-              GenTypeName_ObjectAPI(type.struct_def->name, opts);
+          auto new_type_name = namer_.ObjectType(*type.struct_def);
           type_name.replace(type_name.length() - type_name_length,
                             type_name_length, new_type_name);
         } else if (type.element == BASE_TYPE_UNION) {
-          type_name = WrapInNameSpace(*type.enum_def) + "Union";
+          type_name = namer_.NamespacedType(*type.enum_def) + "Union";
         }
         break;
       }
 
       case BASE_TYPE_UNION: {
-        type_name = WrapInNameSpace(*type.enum_def) + "Union";
+        type_name = namer_.NamespacedType(*type.enum_def) + "Union";
         break;
       }
       default: break;
@@ -2027,71 +2022,71 @@
     return type_name;
   }
 
-  void GenStruct_ObjectAPI(StructDef &struct_def, std::string *code_ptr,
-                           const IDLOptions &opts) const {
+  void GenStruct_ObjectAPI(const StructDef &struct_def,
+                           std::string &code) const {
     if (struct_def.generated) return;
-    auto &code = *code_ptr;
     if (struct_def.attributes.Lookup("private")) {
       // For Java, we leave the enum unmarked to indicate package-private
     } else {
       code += "public ";
     }
 
-    auto class_name = GenTypeName_ObjectAPI(struct_def.name, opts);
+    const auto class_name = namer_.ObjectType(struct_def);
     code += "class " + class_name;
     code += " {\n";
     // Generate Properties
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
-      auto type_name =
-          GenTypeGet_ObjectAPI(field.value.type, opts, false, true);
+      auto type_name = GenTypeGet_ObjectAPI(field.value.type, false, true);
       if (field.IsScalarOptional())
         type_name = ConvertPrimitiveTypeToObjectWrapper_ObjectAPI(type_name);
-      auto camel_name = ConvertCase(field.name, Case::kLowerCamel);
-      code += "  private " + type_name + " " + camel_name + ";\n";
+      const auto field_name = namer_.Field(field);
+      code += "  private " + type_name + " " + field_name + ";\n";
     }
     // Generate Java getters and setters
     code += "\n";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
-      auto type_name =
-          GenTypeGet_ObjectAPI(field.value.type, opts, false, true);
+      const auto field_name = namer_.Field(field);
+      const auto get_field = namer_.Method("get", field);
+      auto type_name = GenTypeGet_ObjectAPI(field.value.type, false, true);
       if (field.IsScalarOptional())
         type_name = ConvertPrimitiveTypeToObjectWrapper_ObjectAPI(type_name);
-      auto camel_name = ConvertCase(field.name, Case::kLowerCamel);
-      code += "  public " + type_name + " " +
-              GenGetterFuncName_ObjectAPI(field.name) + "() { return " +
-              camel_name + "; }\n\n";
+
+      code += "  public " + type_name + " " + get_field + "() { return " +
+              field_name + "; }\n\n";
       std::string array_validation = "";
       if (field.value.type.base_type == BASE_TYPE_ARRAY) {
         array_validation =
-            "if (" + camel_name + " != null && " + camel_name +
+            "if (" + field_name + " != null && " + field_name +
             ".length == " + NumToString(field.value.type.fixed_length) + ") ";
       }
-      code += "  public void " + GenSetterFuncName_ObjectAPI(field.name) + "(" +
-              type_name + " " + camel_name + ") { " + array_validation +
-              "this." + camel_name + " = " + camel_name + "; }\n\n";
+      code += "  public void " + namer_.Method("set", field) + "(" + type_name +
+              " " + field_name + ") { " + array_validation + "this." +
+              field_name + " = " + field_name + "; }\n\n";
     }
     // Generate Constructor
     code += "\n";
     code += "  public " + class_name + "() {\n";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
-      code += "    this." + ConvertCase(field.name, Case::kLowerCamel) + " = ";
-      auto type_name =
-          GenTypeGet_ObjectAPI(field.value.type, opts, false, true);
+      const auto get_field = namer_.Method("get", field);
+
+      code += "    this." + namer_.Field(field) + " = ";
+      const auto type_name =
+          GenTypeGet_ObjectAPI(field.value.type, false, true);
       if (IsScalar(field.value.type.base_type)) {
         if (field.IsScalarOptional()) {
           code += "null;\n";
@@ -2122,15 +2117,18 @@
     }
     code += "  }\n";
     if (parser_.root_struct_def_ == &struct_def) {
+      const std::string struct_type = namer_.Type(struct_def);
       code += "  public static " + class_name +
               " deserializeFromBinary(byte[] fbBuffer) {\n";
-      code += "    return " + struct_def.name + ".getRootAs" + struct_def.name +
+      code += "    return " + struct_type + "." +
+              namer_.LegacyJavaMethod2("getRootAs", struct_def, "") +
               "(ByteBuffer.wrap(fbBuffer)).unpack();\n";
       code += "  }\n";
       code += "  public byte[] serializeToBinary() {\n";
       code += "    FlatBufferBuilder fbb = new FlatBufferBuilder();\n";
-      code += "    " + struct_def.name + ".finish" + struct_def.name +
-              "Buffer(fbb, " + struct_def.name + ".pack(fbb, this));\n";
+      code += "    " + struct_type + "." +
+              namer_.LegacyJavaMethod2("finish", struct_def, "Buffer") +
+              "(fbb, " + struct_type + ".pack(fbb, this));\n";
       code += "    return fbb.sizedByteArray();\n";
       code += "  }\n";
     }
@@ -2140,6 +2138,7 @@
   // This tracks the current namespace used to determine if a type need to be
   // prefixed by its namespace
   const Namespace *cur_name_space_;
+  const IdlNamer namer_;
 };
 }  // namespace java
 
diff --git a/src/idl_gen_json_schema.cpp b/src/idl_gen_json_schema.cpp
index 9ea37ae..5cb6a9d 100644
--- a/src/idl_gen_json_schema.cpp
+++ b/src/idl_gen_json_schema.cpp
@@ -24,7 +24,10 @@
 
 namespace jsons {
 
-template<class T> std::string GenFullName(const T *enum_def) {
+namespace {
+
+template<class T>
+static std::string GenFullName(const T *enum_def) {
   std::string full_name;
   const auto &name_spaces = enum_def->defined_namespace->components;
   for (auto ns = name_spaces.cbegin(); ns != name_spaces.cend(); ++ns) {
@@ -34,15 +37,16 @@
   return full_name;
 }
 
-template<class T> std::string GenTypeRef(const T *enum_def) {
+template<class T>
+static std::string GenTypeRef(const T *enum_def) {
   return "\"$ref\" : \"#/definitions/" + GenFullName(enum_def) + "\"";
 }
 
-std::string GenType(const std::string &name) {
+static std::string GenType(const std::string &name) {
   return "\"type\" : \"" + name + "\"";
 }
 
-std::string GenType(BaseType type) {
+static std::string GenType(BaseType type) {
   switch (type) {
     case BASE_TYPE_BOOL: return "\"type\" : \"boolean\"";
     case BASE_TYPE_CHAR:
@@ -84,13 +88,13 @@
   }
 }
 
-std::string GenBaseType(const Type &type) {
+static std::string GenBaseType(const Type &type) {
   if (type.struct_def != nullptr) { return GenTypeRef(type.struct_def); }
   if (type.enum_def != nullptr) { return GenTypeRef(type.enum_def); }
   return GenType(type.base_type);
 }
 
-std::string GenArrayType(const Type &type) {
+static std::string GenArrayType(const Type &type) {
   std::string element_type;
   if (type.struct_def != nullptr) {
     element_type = GenTypeRef(type.struct_def);
@@ -103,7 +107,7 @@
   return "\"type\" : \"array\", \"items\" : {" + element_type + "}";
 }
 
-std::string GenType(const Type &type) {
+static std::string GenType(const Type &type) {
   switch (type.base_type) {
     case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();  // fall thru
     case BASE_TYPE_VECTOR: {
@@ -136,6 +140,8 @@
   }
 }
 
+} // namespace
+
 class JsonSchemaGenerator : public BaseGenerator {
  private:
   std::string code_;
diff --git a/src/idl_gen_kotlin.cpp b/src/idl_gen_kotlin.cpp
index fc55aeb..102e24d 100644
--- a/src/idl_gen_kotlin.cpp
+++ b/src/idl_gen_kotlin.cpp
@@ -23,11 +23,14 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
+#include "idl_namer.h"
 
 namespace flatbuffers {
 
 namespace kotlin {
 
+namespace {
+
 typedef std::map<std::string, std::pair<std::string, std::string> > FbbParamMap;
 static TypedFloatConstantGenerator KotlinFloatGen("Double.", "Float.", "NaN",
                                                   "POSITIVE_INFINITY",
@@ -35,42 +38,54 @@
 
 static const CommentConfig comment_config = { "/**", " *", " */" };
 static const std::string ident_pad = "    ";
-static const char *keywords[] = {
-  "package",  "as",     "typealias", "class",  "this",   "super",
-  "val",      "var",    "fun",       "for",    "null",   "true",
-  "false",    "is",     "in",        "throw",  "return", "break",
-  "continue", "object", "if",        "try",    "else",   "while",
-  "do",       "when",   "interface", "typeof", "Any",    "Character"
-};
-
-// Escape Keywords
-static std::string Esc(const std::string &name) {
-  for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
-    if (name == keywords[i]) {
-      return ConvertCase(name + "_", Case::kLowerCamel);
-    }
-  }
-
-  return ConvertCase(name, Case::kLowerCamel);
+static std::set<std::string> KotlinKeywords() {
+  return { "package",  "as",     "typealias", "class",  "this",   "super",
+           "val",      "var",    "fun",       "for",    "null",   "true",
+           "false",    "is",     "in",        "throw",  "return", "break",
+           "continue", "object", "if",        "try",    "else",   "while",
+           "do",       "when",   "interface", "typeof", "Any",    "Character" };
 }
 
+static Namer::Config KotlinDefaultConfig() {
+  return { /*types=*/Case::kKeep,
+           /*constants=*/Case::kKeep,
+           /*methods=*/Case::kLowerCamel,
+           /*functions=*/Case::kKeep,
+           /*fields=*/Case::kLowerCamel,
+           /*variables=*/Case::kLowerCamel,
+           /*variants=*/Case::kLowerCamel,
+           /*enum_variant_seperator=*/"",  // I.e. Concatenate.
+           /*escape_keywords=*/Namer::Config::Escape::BeforeConvertingCase,
+           /*namespaces=*/Case::kKeep,
+           /*namespace_seperator=*/"__",
+           /*object_prefix=*/"",
+           /*object_suffix=*/"T",
+           /*keyword_prefix=*/"",
+           /*keyword_suffix=*/"_",
+           /*filenames=*/Case::kKeep,
+           /*directories=*/Case::kKeep,
+           /*output_path=*/"",
+           /*filename_suffix=*/"",
+           /*filename_extension=*/".kt" };
+}
+} // namespace
+
 class KotlinGenerator : public BaseGenerator {
  public:
   KotlinGenerator(const Parser &parser, const std::string &path,
                   const std::string &file_name)
       : BaseGenerator(parser, path, file_name, "", ".", "kt"),
-        cur_name_space_(nullptr) {}
+        namer_(WithFlagOptions(KotlinDefaultConfig(), parser.opts, path),
+               KotlinKeywords()) {}
 
   KotlinGenerator &operator=(const KotlinGenerator &);
   bool generate() FLATBUFFERS_OVERRIDE {
     std::string one_file_code;
 
-    cur_name_space_ = parser_.current_namespace_;
     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
          ++it) {
       CodeWriter enumWriter(ident_pad);
       auto &enum_def = **it;
-      if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
       GenEnum(enum_def, enumWriter);
       if (parser_.opts.one_file) {
         one_file_code += enumWriter.ToString();
@@ -85,8 +100,6 @@
          it != parser_.structs_.vec.end(); ++it) {
       CodeWriter structWriter(ident_pad);
       auto &struct_def = **it;
-      if (!parser_.opts.one_file)
-        cur_name_space_ = struct_def.defined_namespace;
       GenStruct(struct_def, structWriter, parser_.opts);
       if (parser_.opts.one_file) {
         one_file_code += structWriter.ToString();
@@ -124,14 +137,13 @@
       code += "import com.google.flatbuffers.*\n\n";
     }
     code += classcode;
-    auto filename = NamespaceDir(ns) + defname + ".kt";
+    const std::string dirs = namer_.Directories(ns);
+    EnsureDirExists(dirs);
+    const std::string filename =
+        dirs + namer_.File(defname, /*skips=*/SkipFile::Suffix);
     return SaveFile(filename.c_str(), code, false);
   }
 
-  const Namespace *CurrentNameSpace() const FLATBUFFERS_OVERRIDE {
-    return cur_name_space_;
-  }
-
   static bool IsEnum(const Type &type) {
     return type.enum_def != nullptr && IsInteger(type.base_type);
   }
@@ -261,7 +273,7 @@
     GenerateComment(enum_def.doc_comment, writer, &comment_config);
 
     writer += "@Suppress(\"unused\")";
-    writer += "class " + Esc(enum_def.name) + " private constructor() {";
+    writer += "class " + namer_.Type(enum_def) + " private constructor() {";
     writer.IncrementIdentLevel();
 
     GenerateCompanionObject(writer, [&]() {
@@ -272,7 +284,7 @@
         auto field_type = GenTypeBasic(enum_def.underlying_type.base_type);
         auto val = enum_def.ToString(ev);
         auto suffix = LiteralSuffix(enum_def.underlying_type.base_type);
-        writer.SetValue("name", Esc(ev.name));
+        writer.SetValue("name", namer_.LegacyKotlinVariant(ev));
         writer.SetValue("type", field_type);
         writer.SetValue("val", val + suffix);
         GenerateComment(ev.doc_comment, writer, &comment_config);
@@ -339,8 +351,8 @@
       case BASE_TYPE_UTYPE: return bb_var_name + ".get";
       case BASE_TYPE_BOOL: return "0.toByte() != " + bb_var_name + ".get";
       default:
-        return bb_var_name + ".get" +
-               ConvertCase(GenTypeBasic(type.base_type), Case::kUpperCamel);
+        return bb_var_name + "." +
+               namer_.Method("get", GenTypeBasic(type.base_type));
     }
   }
 
@@ -361,8 +373,7 @@
         case BASE_TYPE_NONE:
         case BASE_TYPE_UTYPE: return "bb.put";
         default:
-          return "bb.put" +
-                 ConvertCase(GenTypeBasic(type.base_type), Case::kUpperCamel);
+          return "bb." + namer_.Method("put", GenTypeBasic(type.base_type));
       }
     }
     return "";
@@ -385,8 +396,8 @@
 
   // Recursively generate arguments for a constructor, to deal with nested
   // structs.
-  static void GenStructArgs(const StructDef &struct_def, CodeWriter &writer,
-                            const char *nameprefix) {
+  void GenStructArgs(const StructDef &struct_def, CodeWriter &writer,
+                     const char *nameprefix) const {
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
       auto &field = **it;
@@ -399,7 +410,7 @@
                       (nameprefix + (field.name + "_")).c_str());
       } else {
         writer += std::string(", ") + nameprefix + "\\";
-        writer += ConvertCase(field.name, Case::kUpperCamel) + ": \\";
+        writer += namer_.Field(field) + ": \\";
         writer += GenTypeBasic(field.value.type.base_type) + "\\";
       }
     }
@@ -408,8 +419,8 @@
   // Recusively generate struct construction statements of the form:
   // builder.putType(name);
   // and insert manual padding.
-  static void GenStructBody(const StructDef &struct_def, CodeWriter &writer,
-                            const char *nameprefix) {
+  void GenStructBody(const StructDef &struct_def, CodeWriter &writer,
+                     const char *nameprefix) const {
     writer.SetValue("align", NumToString(struct_def.minalign));
     writer.SetValue("size", NumToString(struct_def.bytesize));
     writer += "builder.prep({{align}}, {{size}})";
@@ -426,8 +437,7 @@
                       (nameprefix + (field.name + "_")).c_str());
       } else {
         writer.SetValue("type", GenMethod(field.value.type));
-        writer.SetValue("argname", nameprefix + ConvertCase(Esc(field.name),
-                                                            Case::kLowerCamel));
+        writer.SetValue("argname", nameprefix + namer_.Variable(field));
         writer.SetValue("cast", CastToSigned(field.value.type));
         writer += "builder.put{{type}}({{argname}}{{cast}})";
       }
@@ -461,7 +471,7 @@
     GenerateComment(struct_def.doc_comment, writer, &comment_config);
     auto fixed = struct_def.fixed;
 
-    writer.SetValue("struct_name", Esc(struct_def.name));
+    writer.SetValue("struct_name", namer_.Type(struct_def));
     writer.SetValue("superclass", fixed ? "Struct" : "Table");
 
     writer += "@Suppress(\"unused\")";
@@ -477,7 +487,7 @@
 
       // Generate assign method
       GenerateFun(writer, "__assign", "_i: Int, _bb: ByteBuffer",
-                  Esc(struct_def.name), [&]() {
+                  namer_.Type(struct_def), [&]() {
                     writer += "__init(_i, _bb)";
                     writer += "return this";
                   });
@@ -490,15 +500,15 @@
         if (!struct_def.fixed) {
           FieldDef *key_field = nullptr;
 
-          // Generate verson check method.
+          // Generate version check method.
           // Force compile time error if not using the same version
           // runtime.
           GenerateFunOneLine(
               writer, "validateVersion", "", "",
-              [&]() { writer += "Constants.FLATBUFFERS_2_0_0()"; },
+              [&]() { writer += "Constants.FLATBUFFERS_2_0_8()"; },
               options.gen_jvmstatic);
 
-          GenerateGetRootAsAccessors(Esc(struct_def.name), writer, options);
+          GenerateGetRootAsAccessors(namer_.Type(struct_def), writer, options);
           GenerateBufferHasIdentifier(struct_def, writer, options);
           GenerateTableCreator(struct_def, writer, options);
 
@@ -550,7 +560,7 @@
   void GenerateLookupByKey(FieldDef *key_field, StructDef &struct_def,
                            CodeWriter &writer, const IDLOptions options) const {
     std::stringstream params;
-    params << "obj: " << Esc(struct_def.name) << "?"
+    params << "obj: " << namer_.Type(struct_def) << "?"
            << ", ";
     params << "vectorLocation: Int, ";
     params << "key: " << GenTypeGet(key_field->value.type) << ", ";
@@ -558,7 +568,7 @@
 
     auto statements = [&]() {
       auto base_type = key_field->value.type.base_type;
-      writer.SetValue("struct_name", Esc(struct_def.name));
+      writer.SetValue("struct_name", namer_.Type(struct_def));
       if (base_type == BASE_TYPE_STRING) {
         writer +=
             "val byteKey = key."
@@ -604,7 +614,8 @@
       writer += "return null";
     };
     GenerateFun(writer, "__lookup_by_key", params.str(),
-                Esc(struct_def.name) + "?", statements, options.gen_jvmstatic);
+                namer_.Type(struct_def) + "?", statements,
+                options.gen_jvmstatic);
   }
 
   void GenerateFinishSizePrefixed(StructDef &struct_def,
@@ -613,7 +624,8 @@
                                   const IDLOptions options) const {
     auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
     auto params = "builder: FlatBufferBuilder, offset: Int";
-    auto method_name = "finishSizePrefixed" + Esc(struct_def.name) + "Buffer";
+    auto method_name =
+        namer_.LegacyJavaMethod2("finishSizePrefixed", struct_def, "Buffer");
     GenerateFunOneLine(
         writer, method_name, params, "",
         [&]() { writer += "builder.finishSizePrefixed(offset" + id + ")"; },
@@ -625,7 +637,8 @@
                                   const IDLOptions options) const {
     auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
     auto params = "builder: FlatBufferBuilder, offset: Int";
-    auto method_name = "finish" + Esc(struct_def.name) + "Buffer";
+    auto method_name =
+        namer_.LegacyKotlinMethod("finish", struct_def, "Buffer");
     GenerateFunOneLine(
         writer, method_name, params, "",
         [&]() { writer += "builder.finish(offset" + id + ")"; },
@@ -635,7 +648,7 @@
   void GenerateEndStructMethod(StructDef &struct_def, CodeWriter &writer,
                                const IDLOptions options) const {
     // Generate end{{TableName}}(builder: FlatBufferBuilder) method
-    auto name = "end" + Esc(struct_def.name);
+    auto name = namer_.LegacyJavaMethod2("end", struct_def, "");
     auto params = "builder: FlatBufferBuilder";
     auto returns = "Int";
     auto field_vec = struct_def.fields.vec;
@@ -661,8 +674,7 @@
   void GenerateCreateVectorField(FieldDef &field, CodeWriter &writer,
                                  const IDLOptions options) const {
     auto vector_type = field.value.type.VectorType();
-    auto method_name =
-        "create" + ConvertCase(Esc(field.name), Case::kUpperCamel) + "Vector";
+    auto method_name = namer_.Method("create", field, "vector");
     auto params = "builder: FlatBufferBuilder, data: " +
                   GenTypeBasic(vector_type.base_type) + "Array";
     writer.SetValue("size", NumToString(InlineSize(vector_type)));
@@ -694,9 +706,7 @@
     writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
 
     GenerateFunOneLine(
-        writer,
-        "start" + ConvertCase(Esc(field.name) + "Vector", Case::kUpperCamel),
-        params, "",
+        writer, namer_.Method("start", field, "Vector"), params, "",
         [&]() {
           writer += "builder.startVector({{size}}, numElems, {{align}})";
         },
@@ -706,25 +716,36 @@
   void GenerateAddField(std::string field_pos, FieldDef &field,
                         CodeWriter &writer, const IDLOptions options) const {
     auto field_type = GenTypeBasic(field.value.type.base_type);
-    auto secondArg =
-        ConvertCase(Esc(field.name), Case::kLowerCamel) + ": " + field_type;
+    auto secondArg = namer_.Variable(field.name) + ": " + field_type;
 
-    GenerateFunOneLine(
-        writer, "add" + ConvertCase(Esc(field.name), Case::kUpperCamel),
-        "builder: FlatBufferBuilder, " + secondArg, "",
-        [&]() {
-          auto method = GenMethod(field.value.type);
-          writer.SetValue("field_name",
-                          ConvertCase(Esc(field.name), Case::kLowerCamel));
-          writer.SetValue("method_name", method);
-          writer.SetValue("pos", field_pos);
-          writer.SetValue("default", GenFBBDefaultValue(field));
-          writer.SetValue("cast", GenFBBValueCast(field));
-
-          writer += "builder.add{{method_name}}({{pos}}, \\";
-          writer += "{{field_name}}{{cast}}, {{default}})";
-        },
-        options.gen_jvmstatic);
+    auto content = [&]() {
+      auto method = GenMethod(field.value.type);
+      writer.SetValue("field_name", namer_.Field(field));
+      writer.SetValue("method_name", method);
+      writer.SetValue("pos", field_pos);
+      writer.SetValue("default", GenFBBDefaultValue(field));
+      writer.SetValue("cast", GenFBBValueCast(field));
+      if (field.key) {
+        // field has key attribute, so always need to exist
+        // even if its value is equal to default.
+        // Generated code will bypass default checking
+        // resulting in { builder.addShort(name); slot(id); }
+        writer += "builder.add{{method_name}}({{field_name}}{{cast}})";
+        writer += "builder.slot({{pos}})";
+      } else {
+        writer += "builder.add{{method_name}}({{pos}}, \\";
+        writer += "{{field_name}}{{cast}}, {{default}})";
+      }
+    };
+    auto signature = namer_.LegacyKotlinMethod("add", field, "");
+    auto params = "builder: FlatBufferBuilder, " + secondArg;
+    if (field.key) {
+      GenerateFun(writer, signature, params, "", content,
+                  options.gen_jvmstatic);
+    } else {
+      GenerateFunOneLine(writer, signature, params, "", content,
+                         options.gen_jvmstatic);
+    }
   }
 
   static std::string ToSignedType(const Type &type) {
@@ -768,7 +789,8 @@
   void GenerateStartStructMethod(StructDef &struct_def, CodeWriter &code,
                                  const IDLOptions options) const {
     GenerateFunOneLine(
-        code, "start" + Esc(struct_def.name), "builder: FlatBufferBuilder", "",
+        code, namer_.LegacyJavaMethod2("start", struct_def, ""),
+        "builder: FlatBufferBuilder", "",
         [&]() {
           code += "builder.startTable(" +
                   NumToString(struct_def.fields.vec.size()) + ")";
@@ -800,13 +822,13 @@
       // Generate a table constructor of the form:
       // public static int createName(FlatBufferBuilder builder, args...)
 
-      auto name = "create" + Esc(struct_def.name);
+      auto name = namer_.LegacyJavaMethod2("create", struct_def, "");
       std::stringstream params;
       params << "builder: FlatBufferBuilder";
       for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
         auto &field = **it;
         if (field.deprecated) continue;
-        params << ", " << ConvertCase(Esc(field.name), Case::kLowerCamel);
+        params << ", " << namer_.Variable(field);
         if (!IsScalar(field.value.type.base_type)) {
           params << "Offset: ";
         } else {
@@ -832,18 +854,15 @@
                 auto base_type_size = SizeOf(field.value.type.base_type);
                 if (!field.deprecated &&
                     (!sortbysize || size == base_type_size)) {
-                  writer.SetValue(
-                      "camel_field_name",
-                      ConvertCase(Esc(field.name), Case::kUpperCamel));
-                  writer.SetValue("field_name", ConvertCase(Esc(field.name),
-                                                            Case::kLowerCamel));
+                  writer.SetValue("field_name", namer_.Field(field));
 
                   // we wrap on null check for scalar optionals
                   writer += field.IsScalarOptional()
                                 ? "{{field_name}}?.run { \\"
                                 : "\\";
 
-                  writer += "add{{camel_field_name}}(builder, {{field_name}}\\";
+                  writer += namer_.LegacyKotlinMethod("add", field, "") +
+                            "(builder, {{field_name}}\\";
                   if (!IsScalar(field.value.type.base_type)) {
                     writer += "Offset\\";
                   }
@@ -863,7 +882,7 @@
     // Check if a buffer has the identifier.
     if (parser_.root_struct_def_ != &struct_def || !file_identifier.length())
       return;
-    auto name = ConvertCase(Esc(struct_def.name), Case::kLowerCamel);
+    auto name = namer_.Function(struct_def);
     GenerateFunOneLine(
         writer, name + "BufferHasIdentifier", "_bb: ByteBuffer", "Boolean",
         [&]() {
@@ -882,7 +901,7 @@
 
       GenerateComment(field.doc_comment, writer, &comment_config);
 
-      auto field_name = ConvertCase(Esc(field.name), Case::kLowerCamel);
+      auto field_name = namer_.Field(field);
       auto field_type = GenTypeGet(field.value.type);
       auto field_default_value = GenDefaultValue(field);
       auto return_type = GetterReturnType(field);
@@ -1053,8 +1072,7 @@
             auto &kfield = **kit;
             if (kfield.key) {
               auto qualified_name = WrapInNameSpace(sd);
-              auto name =
-                  ConvertCase(Esc(field.name), Case::kLowerCamel) + "ByKey";
+              auto name = namer_.Method(field, "ByKey");
               auto params = "key: " + GenTypeGet(kfield.value.type);
               auto rtype = qualified_name + "?";
               GenerateFun(writer, name, params, rtype, [&]() {
@@ -1147,9 +1165,9 @@
         auto underlying_type = value_base_type == BASE_TYPE_VECTOR
                                    ? value_type.VectorType()
                                    : value_type;
-        auto name = "mutate" + ConvertCase(Esc(field.name), Case::kUpperCamel);
+        auto name = namer_.LegacyKotlinMethod("mutate", field, "");
         auto size = NumToString(InlineSize(underlying_type));
-        auto params = Esc(field.name) + ": " + GenTypeGet(underlying_type);
+        auto params = namer_.Field(field) + ": " + GenTypeGet(underlying_type);
         // A vector mutator also needs the index of the vector element it should
         // mutate.
         if (value_base_type == BASE_TYPE_VECTOR) params.insert(0, "j: Int, ");
@@ -1158,8 +1176,8 @@
         // representation.
         auto setter_parameter =
             underlying_type.base_type == BASE_TYPE_BOOL
-                ? "(if(" + Esc(field.name) + ") 1 else 0).toByte()"
-                : Esc(field.name);
+                ? "(if(" + namer_.Field(field) + ") 1 else 0).toByte()"
+                : namer_.Field(field);
 
         auto setter_index =
             value_base_type == BASE_TYPE_VECTOR
@@ -1292,9 +1310,9 @@
     }
   }
 
-  static void GenerateGetRootAsAccessors(const std::string &struct_name,
-                                         CodeWriter &writer,
-                                         IDLOptions options) {
+  void GenerateGetRootAsAccessors(const std::string &struct_name,
+                                  CodeWriter &writer,
+                                  IDLOptions options) const {
     // Generate a special accessor for the table that when used as the root
     // ex: fun getRootAsMonster(_bb: ByteBuffer): Monster {...}
     writer.SetValue("gr_name", struct_name);
@@ -1320,13 +1338,12 @@
     writer += "}";
   }
 
-  static void GenerateStaticConstructor(const StructDef &struct_def,
-                                        CodeWriter &code,
-                                        const IDLOptions options) {
+  void GenerateStaticConstructor(const StructDef &struct_def, CodeWriter &code,
+                                 const IDLOptions options) const {
     // create a struct constructor function
     auto params = StructConstructorParams(struct_def);
     GenerateFun(
-        code, "create" + Esc(struct_def.name), params, "Int",
+        code, namer_.LegacyJavaMethod2("create", struct_def, ""), params, "Int",
         [&]() {
           GenStructBody(struct_def, code, "");
           code += "return builder.offset()";
@@ -1334,8 +1351,8 @@
         options.gen_jvmstatic);
   }
 
-  static std::string StructConstructorParams(const StructDef &struct_def,
-                                             const std::string &prefix = "") {
+  std::string StructConstructorParams(const StructDef &struct_def,
+                                      const std::string &prefix = "") const {
     // builder: FlatBufferBuilder
     std::stringstream out;
     auto field_vec = struct_def.fields.vec;
@@ -1348,10 +1365,10 @@
         // constructing a nested struct, prefix the name with the field
         // name.
         out << StructConstructorParams(*field.value.type.struct_def,
-                                       prefix + (Esc(field.name) + "_"));
+                                       prefix + (namer_.Variable(field) + "_"));
       } else {
-        out << ", " << prefix << ConvertCase(Esc(field.name), Case::kLowerCamel)
-            << ": " << GenTypeBasic(field.value.type.base_type);
+        out << ", " << prefix << namer_.Variable(field) << ": "
+            << GenTypeBasic(field.value.type.base_type);
       }
     }
     return out.str();
@@ -1520,9 +1537,7 @@
     if (gen_jvmstatic) { code += "@JvmStatic"; }
   }
 
-  // This tracks the current namespace used to determine if a type need to be
-  // prefixed by its namespace
-  const Namespace *cur_name_space_;
+  const IdlNamer namer_;
 };
 }  // namespace kotlin
 
diff --git a/src/idl_gen_lobster.cpp b/src/idl_gen_lobster.cpp
index c1e78ad..67830f3 100644
--- a/src/idl_gen_lobster.cpp
+++ b/src/idl_gen_lobster.cpp
@@ -72,9 +72,11 @@
 
   std::string LobsterType(const Type &type) {
     if (IsFloat(type.base_type)) return "float";
+    if (IsBool(type.base_type)) return "bool";
     if (IsScalar(type.base_type) && type.enum_def)
       return NormalizedName(*type.enum_def);
     if (!IsScalar(type.base_type)) return "flatbuffers_offset";
+    if (IsString(type)) return "string";
     return "int";
   }
 
@@ -116,21 +118,27 @@
         auto defval = field.IsOptional() ? "0" : field.value.constant;
         acc = "buf_.flatbuffers_field_" + GenTypeName(field.value.type) +
               "(pos_, " + offsets + ", " + defval + ")";
+        if (IsBool(field.value.type.base_type))
+          acc = "bool(" + acc + ")";
       }
       if (field.value.type.enum_def)
         acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")";
-      if (field.IsOptional())
+      if (field.IsOptional()) {
         acc += ", buf_.flatbuffers_field_present(pos_, " + offsets + ")";
-      code += def + "():\n        return " + acc + "\n";
+        code += def + "() -> " + LobsterType(field.value.type) + ", bool:\n        return " + acc + "\n";
+      } else {
+        code += def + "() -> " + LobsterType(field.value.type) + ":\n        return " + acc + "\n";
+      }
       return;
     }
     switch (field.value.type.base_type) {
       case BASE_TYPE_STRUCT: {
         auto name = NamespacedName(*field.value.type.struct_def);
-        code += def + "():\n        ";
         if (struct_def.fixed) {
+          code += def + "() -> " + name + ":\n        ";
           code += "return " + name + "{ buf_, pos_ + " + offsets + " }\n";
         } else {
+          code += def + "() -> " + name + "?:\n        ";
           code += std::string("let o = buf_.flatbuffers_field_") +
                   (field.value.type.struct_def->fixed ? "struct" : "table") +
                   "(pos_, " + offsets + ")\n        return if o: " + name +
@@ -140,25 +148,28 @@
       }
       case BASE_TYPE_STRING:
         code += def +
-                "():\n        return buf_.flatbuffers_field_string(pos_, " +
+                "() -> string:\n        return buf_.flatbuffers_field_string(pos_, " +
                 offsets + ")\n";
         break;
       case BASE_TYPE_VECTOR: {
         auto vectortype = field.value.type.VectorType();
-        code += def + "(i:int):\n        return ";
         if (vectortype.base_type == BASE_TYPE_STRUCT) {
           auto start = "buf_.flatbuffers_field_vector(pos_, " + offsets +
                        ") + i * " + NumToString(InlineSize(vectortype));
           if (!(vectortype.struct_def->fixed)) {
             start = "buf_.flatbuffers_indirect(" + start + ")";
           }
+          code += def + "(i:int) -> " + NamespacedName(*field.value.type.struct_def) + ":\n        return ";
           code += NamespacedName(*field.value.type.struct_def) + " { buf_, " +
                   start + " }\n";
         } else {
-          if (IsString(vectortype))
+          if (IsString(vectortype)) {
+            code += def + "(i:int) -> string:\n        return ";
             code += "buf_.flatbuffers_string";
-          else
+          } else {
+            code += def + "(i:int) -> " + LobsterType(vectortype) + ":\n        return ";
             code += "buf_.read_" + GenTypeName(vectortype) + "_le";
+          }
           code += "(buf_.flatbuffers_field_vector(pos_, " + offsets +
                   ") + i * " + NumToString(InlineSize(vectortype)) + ")\n";
         }
@@ -181,7 +192,7 @@
     }
     if (IsVector(field.value.type)) {
       code += def +
-              "_length():\n        return "
+              "_length() -> int:\n        return "
               "buf_.flatbuffers_field_vector_len(pos_, " +
               offsets + ")\n";
     }
diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp
index 8426b6e..0b8ffa8 100644
--- a/src/idl_gen_python.cpp
+++ b/src/idl_gen_python.cpp
@@ -26,12 +26,14 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
-#include "namer.h"
+#include "idl_namer.h"
 
 namespace flatbuffers {
 namespace python {
 
-std::set<std::string> PythonKeywords() {
+namespace {
+
+static std::set<std::string> PythonKeywords() {
   return { "False", "None",   "True",     "and",   "as",     "assert",
            "break", "class",  "continue", "def",   "del",    "elif",
            "else",  "except", "finally",  "for",   "from",   "global",
@@ -40,7 +42,7 @@
            "while", "with",   "yield" };
 }
 
-Namer::Config PythonDefaultConfig() {
+static Namer::Config PythonDefaultConfig() {
   return { /*types=*/Case::kKeep,
            /*constants=*/Case::kScreamingSnake,
            /*methods=*/Case::kUpperCamel,
@@ -49,6 +51,7 @@
            /*variable=*/Case::kLowerCamel,
            /*variants=*/Case::kKeep,
            /*enum_variant_seperator=*/".",
+           /*escape_keywords=*/Namer::Config::Escape::BeforeConvertingCase,
            /*namespaces=*/Case::kKeep,  // Packages in python.
            /*namespace_seperator=*/".",
            /*object_prefix=*/"",
@@ -63,8 +66,10 @@
 }
 
 // Hardcode spaces per indentation.
-const CommentConfig def_comment = { nullptr, "#", nullptr };
-const std::string Indent = "    ";
+static const CommentConfig def_comment = { nullptr, "#", nullptr };
+static const std::string Indent = "    ";
+
+} // namespace
 
 class PythonGenerator : public BaseGenerator {
  public:
@@ -73,8 +78,8 @@
       : BaseGenerator(parser, path, file_name, "" /* not used */,
                       "" /* not used */, "py"),
         float_const_gen_("float('nan')", "float('inf')", "float('-inf')"),
-        namer_({ PythonDefaultConfig().WithFlagOptions(parser.opts, path),
-                 PythonKeywords() }) {}
+        namer_(WithFlagOptions(PythonDefaultConfig(), parser.opts, path),
+               PythonKeywords()) {}
 
   // Most field accessors need to retrieve and test the field offset first,
   // this is the prefix code for that.
@@ -88,7 +93,7 @@
   // Begin a class declaration.
   void BeginClass(const StructDef &struct_def, std::string *code_ptr) const {
     auto &code = *code_ptr;
-    code += "class " + namer_.Type(struct_def.name) + "(object):\n";
+    code += "class " + namer_.Type(struct_def) + "(object):\n";
     code += Indent + "__slots__ = ['_tab']";
     code += "\n\n";
   }
@@ -96,7 +101,7 @@
   // Begin enum code with a class declaration.
   void BeginEnum(const EnumDef &enum_def, std::string *code_ptr) const {
     auto &code = *code_ptr;
-    code += "class " + namer_.Type(enum_def.name) + "(object):\n";
+    code += "class " + namer_.Type(enum_def) + "(object):\n";
   }
 
   // Starts a new line and then indents.
@@ -109,7 +114,7 @@
                   std::string *code_ptr) const {
     auto &code = *code_ptr;
     code += Indent;
-    code += namer_.Variant(ev.name);
+    code += namer_.Variant(ev);
     code += " = ";
     code += enum_def.ToString(ev) + "\n";
   }
@@ -118,7 +123,7 @@
   void NewRootTypeFromBuffer(const StructDef &struct_def,
                              std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const std::string struct_type = namer_.Type(struct_def.name);
+    const std::string struct_type = namer_.Type(struct_def);
 
     code += Indent + "@classmethod\n";
     code += Indent + "def GetRootAs";
@@ -158,7 +163,7 @@
     auto &code = *code_ptr;
 
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name) + "Length(self";
+    code += namer_.Method(field) + "Length(self";
     code += "):" + OffsetPrefix(field);
     code += Indent + Indent + Indent + "return self._tab.VectorLen(o)\n";
     code += Indent + Indent + "return 0\n\n";
@@ -170,7 +175,7 @@
     auto &code = *code_ptr;
 
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name) + "IsNone(self";
+    code += namer_.Method(field) + "IsNone(self";
     code += "):";
     code += GenIndents(2) +
             "o = flatbuffers.number_types.UOffsetTFlags.py_type" +
@@ -186,7 +191,7 @@
     auto &code = *code_ptr;
     std::string getter = GenGetter(field.value.type);
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name);
+    code += namer_.Method(field);
     code += "(self): return " + getter;
     code += "self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(";
     code += NumToString(field.value.offset) + "))\n";
@@ -198,7 +203,7 @@
     auto &code = *code_ptr;
     std::string getter = GenGetter(field.value.type);
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name);
+    code += namer_.Method(field);
     code += "(self):";
     code += OffsetPrefix(field);
     getter += "o + self._tab.Pos)";
@@ -206,7 +211,9 @@
     if (is_bool) { getter = "bool(" + getter + ")"; }
     code += Indent + Indent + Indent + "return " + getter + "\n";
     std::string default_value;
-    if (is_bool) {
+    if (field.IsScalarOptional()) {
+      default_value = "None";
+    } else if (is_bool) {
       default_value = field.value.constant == "0" ? "False" : "True";
     } else {
       default_value = IsFloat(field.value.type.base_type)
@@ -223,7 +230,7 @@
                               std::string *code_ptr) const {
     auto &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name);
+    code += namer_.Method(field);
     code += "(self, obj):\n";
     code += Indent + Indent + "obj.Init(self._tab.Bytes, self._tab.Pos + ";
     code += NumToString(field.value.offset) + ")";
@@ -236,7 +243,7 @@
     auto &code = *code_ptr;
     const auto vec_type = field.value.type.VectorType();
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name);
+    code += namer_.Method(field);
     if (IsStruct(vec_type)) {
       code += "(self, obj, i):\n";
       code += Indent + Indent + "obj.Init(self._tab.Bytes, self._tab.Pos + ";
@@ -260,7 +267,7 @@
                              std::string *code_ptr) const {
     auto &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name);
+    code += namer_.Method(field);
     code += "(self):";
     code += OffsetPrefix(field);
     if (field.value.type.struct_def->fixed) {
@@ -285,7 +292,7 @@
                       std::string *code_ptr) const {
     auto &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name);
+    code += namer_.Method(field);
     code += "(self):";
     code += OffsetPrefix(field);
     code += Indent + Indent + Indent + "return " + GenGetter(field.value.type);
@@ -298,7 +305,7 @@
                      std::string *code_ptr) const {
     auto &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name) + "(self):";
+    code += namer_.Method(field) + "(self):";
     code += OffsetPrefix(field);
 
     // TODO(rw): this works and is not the good way to it:
@@ -321,21 +328,13 @@
   // module.
   std::string GenPackageReference(const Type &type) const {
     if (type.struct_def) {
-      return GenPackageReference(*type.struct_def);
+      return namer_.NamespacedType(*type.struct_def);
     } else if (type.enum_def) {
-      return GenPackageReference(*type.enum_def);
+      return namer_.NamespacedType(*type.enum_def);
     } else {
       return "." + GenTypeGet(type);
     }
   }
-  std::string GenPackageReference(const EnumDef &enum_def) const {
-    return namer_.NamespacedType(enum_def.defined_namespace->components,
-                                 enum_def.name);
-  }
-  std::string GenPackageReference(const StructDef &struct_def) const {
-    return namer_.NamespacedType(struct_def.defined_namespace->components,
-                                 struct_def.name);
-  }
 
   // Get the value of a vector's struct member.
   void GetMemberOfVectorOfStruct(const StructDef &struct_def,
@@ -345,7 +344,7 @@
     auto vectortype = field.value.type.VectorType();
 
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name);
+    code += namer_.Method(field);
     code += "(self, j):" + OffsetPrefix(field);
     code += Indent + Indent + Indent + "x = self._tab.Vector(o)\n";
     code += Indent + Indent + Indent;
@@ -374,7 +373,7 @@
     auto vectortype = field.value.type.VectorType();
 
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name);
+    code += namer_.Method(field);
     code += "(self, j):";
     code += OffsetPrefix(field);
     code += Indent + Indent + Indent + "a = self._tab.Vector(o)\n";
@@ -403,7 +402,7 @@
     if (!(IsScalar(vectortype.base_type))) { return; }
 
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name) + "AsNumpy(self):";
+    code += namer_.Method(field) + "AsNumpy(self):";
     code += OffsetPrefix(field);
 
     code += Indent + Indent + Indent;
@@ -421,7 +420,7 @@
   }
 
   std::string NestedFlatbufferType(std::string unqualified_name) const {
-    StructDef* nested_root = parser_.LookupStruct(unqualified_name);
+    StructDef *nested_root = parser_.LookupStruct(unqualified_name);
     std::string qualified_name;
     if (nested_root == nullptr) {
       qualified_name = namer_.NamespacedType(
@@ -445,7 +444,7 @@
 
     auto &code = *code_ptr;
     GenReceiver(struct_def, code_ptr);
-    code += namer_.Method(field.name) + "NestedRoot(self):";
+    code += namer_.Method(field) + "NestedRoot(self):";
 
     code += OffsetPrefix(field);
 
@@ -464,7 +463,7 @@
     auto &code = *code_ptr;
 
     code += "\n";
-    code += "def Create" + namer_.Type(struct_def.name);
+    code += "def Create" + namer_.Type(struct_def);
     code += "(builder";
   }
 
@@ -487,14 +486,14 @@
         // a nested struct, prefix the name with the field name.
         auto subprefix = nameprefix;
         if (has_field_name) {
-          subprefix += namer_.Field(field.name) + fieldname_suffix;
+          subprefix += namer_.Field(field) + fieldname_suffix;
         }
         StructBuilderArgs(*field.value.type.struct_def, subprefix, namesuffix,
                           has_field_name, fieldname_suffix, code_ptr);
       } else {
         auto &code = *code_ptr;
         code += std::string(", ") + nameprefix;
-        if (has_field_name) { code += namer_.Field(field.name); }
+        if (has_field_name) { code += namer_.Field(field); }
         code += namesuffix;
       }
     }
@@ -526,10 +525,9 @@
         code +=
             indent + "    builder.Pad(" + NumToString(field.padding) + ")\n";
       if (IsStruct(field_type)) {
-        StructBuilderBody(
-            *field_type.struct_def,
-            (nameprefix + (namer_.Field(field.name) + "_")).c_str(), code_ptr,
-            index, in_array);
+        StructBuilderBody(*field_type.struct_def,
+                          (nameprefix + (namer_.Field(field) + "_")).c_str(),
+                          code_ptr, index, in_array);
       } else {
         const auto index_var = "_idx" + NumToString(index);
         if (IsArray(field_type)) {
@@ -539,14 +537,13 @@
           in_array = true;
         }
         if (IsStruct(type)) {
-          StructBuilderBody(
-              *field_type.struct_def,
-              (nameprefix + (namer_.Field(field.name) + "_")).c_str(), code_ptr,
-              index + 1, in_array);
+          StructBuilderBody(*field_type.struct_def,
+                            (nameprefix + (namer_.Field(field) + "_")).c_str(),
+                            code_ptr, index + 1, in_array);
         } else {
           code += IsArray(field_type) ? "    " : "";
           code += indent + "    builder.Prepend" + GenMethod(field) + "(";
-          code += nameprefix + namer_.Variable(field.name);
+          code += nameprefix + namer_.Variable(field);
           size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
           for (size_t i = 0; in_array && i < array_cnt; i++) {
             code += "[_idx" + NumToString(i) + "-1]";
@@ -566,7 +563,7 @@
   void GetStartOfTable(const StructDef &struct_def,
                        std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto struct_type = namer_.Type(struct_def.name);
+    const auto struct_type = namer_.Type(struct_def);
     // Generate method with struct name.
     code += "def " + struct_type + "Start(builder): ";
     code += "builder.StartObject(";
@@ -584,11 +581,11 @@
   void BuildFieldOfTable(const StructDef &struct_def, const FieldDef &field,
                          const size_t offset, std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const std::string field_var = namer_.Variable(field.name);
-    const std::string field_method = namer_.Method(field.name);
+    const std::string field_var = namer_.Variable(field);
+    const std::string field_method = namer_.Method(field);
 
     // Generate method with struct name.
-    code += "def " + namer_.Type(struct_def.name) + "Add" + field_method;
+    code += "def " + namer_.Type(struct_def) + "Add" + field_method;
     code += "(builder, ";
     code += field_var;
     code += "): ";
@@ -602,16 +599,20 @@
       code += field_var;
     }
     code += ", ";
-    code += IsFloat(field.value.type.base_type)
-                ? float_const_gen_.GenFloatConstant(field)
-                : field.value.constant;
+    if (field.IsScalarOptional()) {
+      code += "None";
+    } else if (IsFloat(field.value.type.base_type)) {
+      code += float_const_gen_.GenFloatConstant(field);
+    } else {
+      code += field.value.constant;
+    }
     code += ")\n";
 
     if (!parser_.opts.one_file) {
       // Generate method without struct name.
       code += "def Add" + field_method + "(builder, " + field_var + "):\n";
-      code += Indent + "return " + namer_.Type(struct_def.name) + "Add" +
-              field_method;
+      code +=
+          Indent + "return " + namer_.Type(struct_def) + "Add" + field_method;
       code += "(builder, ";
       code += field_var;
       code += ")\n";
@@ -622,8 +623,8 @@
   void BuildVectorOfTable(const StructDef &struct_def, const FieldDef &field,
                           std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const std::string struct_type = namer_.Type(struct_def.name);
-    const std::string field_method = namer_.Method(field.name);
+    const std::string struct_type = namer_.Type(struct_def);
+    const std::string field_method = namer_.Method(field);
 
     // Generate method with struct name.
     code += "def " + struct_type + "Start" + field_method;
@@ -653,8 +654,8 @@
     if (!nested) { return; }  // There is no nested flatbuffer.
 
     auto &code = *code_ptr;
-    const std::string field_method = namer_.Method(field.name);
-    const std::string struct_type = namer_.Type(struct_def.name);
+    const std::string field_method = namer_.Method(field);
+    const std::string struct_type = namer_.Type(struct_def);
 
     // Generate method with struct and field name.
     code += "def " + struct_type + "Make" + field_method;
@@ -685,22 +686,21 @@
     auto &code = *code_ptr;
 
     // Generate method with struct name.
-    code += "def " + namer_.Type(struct_def.name) + "End";
+    code += "def " + namer_.Type(struct_def) + "End";
     code += "(builder): ";
     code += "return builder.EndObject()\n";
 
     if (!parser_.opts.one_file) {
       // Generate method without struct name.
       code += "def End(builder):\n";
-      code +=
-          Indent + "return " + namer_.Type(struct_def.name) + "End(builder)";
+      code += Indent + "return " + namer_.Type(struct_def) + "End(builder)";
     }
   }
 
   // Generate the receiver for function signatures.
   void GenReceiver(const StructDef &struct_def, std::string *code_ptr) const {
     auto &code = *code_ptr;
-    code += Indent + "# " + namer_.Type(struct_def.name) + "\n";
+    code += Indent + "# " + namer_.Type(struct_def) + "\n";
     code += Indent + "def ";
   }
 
@@ -795,7 +795,7 @@
     }
 
     code += Indent + "@classmethod\n";
-    code += Indent + "def " + namer_.Type(struct_def.name);
+    code += Indent + "def " + namer_.Type(struct_def);
     code += "BufferHasIdentifier(cls, buf, offset, size_prefixed=False):";
     code += "\n";
     code += Indent + Indent;
@@ -846,7 +846,7 @@
   void GenReceiverForObjectAPI(const StructDef &struct_def,
                                std::string *code_ptr) const {
     auto &code = *code_ptr;
-    code += GenIndents(1) + "# " + namer_.ObjectType(struct_def.name);
+    code += GenIndents(1) + "# " + namer_.ObjectType(struct_def);
     code += GenIndents(1) + "def ";
   }
 
@@ -854,7 +854,7 @@
                               std::string *code_ptr) const {
     auto &code = *code_ptr;
     code += "\n";
-    code += "class " + namer_.ObjectType(struct_def.name) + "(object):";
+    code += "class " + namer_.ObjectType(struct_def) + "(object):";
     code += "\n";
   }
 
@@ -878,7 +878,9 @@
 
   std::string GetDefaultValue(const FieldDef &field) const {
     BaseType base_type = field.value.type.base_type;
-    if (IsBool(base_type)) {
+    if (field.IsScalarOptional()) {
+      return "None";
+    } else if (IsBool(base_type)) {
       return field.value.constant == "0" ? "False" : "True";
     } else if (IsFloat(base_type)) {
       return float_const_gen_.GenFloatConstant(field);
@@ -907,7 +909,7 @@
       std::string field_type;
       switch (ev.union_type.base_type) {
         case BASE_TYPE_STRUCT:
-          field_type = namer_.ObjectType(ev.union_type.struct_def->name);
+          field_type = namer_.ObjectType(*ev.union_type.struct_def);
           if (parser_.opts.include_dependence_headers) {
             auto package_reference = GenPackageReference(ev.union_type);
             field_type = package_reference + "." + field_type;
@@ -927,8 +929,7 @@
 
     // Gets the import lists for the union.
     if (parser_.opts.include_dependence_headers) {
-      const auto package_reference =
-          GenPackageReference(*field.value.type.enum_def);
+      const auto package_reference = GenPackageReference(field.value.type);
       import_list->insert("import " + package_reference);
     }
   }
@@ -939,7 +940,7 @@
     import_typing_list->insert("Optional");
     auto &output = *out_ptr;
     const Type &type = field.value.type;
-    const std::string object_type = namer_.ObjectType(type.struct_def->name);
+    const std::string object_type = namer_.ObjectType(*type.struct_def);
     if (parser_.opts.include_dependence_headers) {
       auto package_reference = GenPackageReference(type);
       output = package_reference + "." + object_type + "]";
@@ -959,7 +960,7 @@
     const BaseType base_type = vector_type.base_type;
     if (base_type == BASE_TYPE_STRUCT) {
       const std::string object_type =
-          namer_.ObjectType(GenTypeGet(vector_type));
+          namer_.ObjectType(*vector_type.struct_def);
       field_type = object_type + "]";
       if (parser_.opts.include_dependence_headers) {
         auto package_reference = GenPackageReference(vector_type);
@@ -1002,12 +1003,15 @@
         default:
           // Scalar or sting fields.
           field_type = GetBasePythonTypeForScalarAndString(base_type);
+          if (field.IsScalarOptional()) {
+            field_type = "Optional[" + field_type + "]";
+          }
           break;
       }
 
       const auto default_value = GetDefaultValue(field);
       // Wrties the init statement.
-      const auto field_field = namer_.Field(field.name);
+      const auto field_field = namer_.Field(field);
       code += GenIndents(2) + "self." + field_field + " = " + default_value +
               "  # type: " + field_type;
     }
@@ -1045,15 +1049,15 @@
     }
 
     // Removes the import of the struct itself, if applied.
-    auto struct_import = "import " + GenPackageReference(struct_def);
+    auto struct_import = "import " + namer_.NamespacedType(struct_def);
     import_list->erase(struct_import);
   }
 
   void InitializeFromBuf(const StructDef &struct_def,
                          std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto struct_var = namer_.Variable(struct_def.name);
-    const auto struct_type = namer_.Type(struct_def.name);
+    const auto struct_var = namer_.Variable(struct_def);
+    const auto struct_type = namer_.Type(struct_def);
 
     code += GenIndents(1) + "@classmethod";
     code += GenIndents(1) + "def InitFromBuf(cls, buf, pos):";
@@ -1066,8 +1070,8 @@
   void InitializeFromObjForObject(const StructDef &struct_def,
                                   std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto struct_var = namer_.Variable(struct_def.name);
-    const auto struct_object = namer_.ObjectType(struct_def.name);
+    const auto struct_var = namer_.Variable(struct_def);
+    const auto struct_object = namer_.ObjectType(struct_def);
 
     code += GenIndents(1) + "@classmethod";
     code += GenIndents(1) + "def InitFromObj(cls, " + struct_var + "):";
@@ -1080,9 +1084,9 @@
   void GenUnPackForStruct(const StructDef &struct_def, const FieldDef &field,
                           std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto struct_var = namer_.Variable(struct_def.name);
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
+    const auto struct_var = namer_.Variable(struct_def);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
     auto field_type = TypeName(field);
 
     if (parser_.opts.include_dependence_headers) {
@@ -1108,14 +1112,14 @@
   void GenUnPackForUnion(const StructDef &struct_def, const FieldDef &field,
                          std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_var = namer_.Variable(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_var = namer_.Variable(struct_def);
     const EnumDef &enum_def = *field.value.type.enum_def;
-    auto union_type = namer_.Namespace(enum_def.name);
+    auto union_type = namer_.Type(enum_def);
 
     if (parser_.opts.include_dependence_headers) {
-      union_type = GenPackageReference(enum_def) + "." + union_type;
+      union_type = namer_.NamespacedType(enum_def) + "." + union_type;
     }
     code += GenIndents(2) + "self." + field_field + " = " + union_type +
             "Creator(" + "self." + field_field + "Type, " + struct_var + "." +
@@ -1126,9 +1130,9 @@
                                 const FieldDef &field,
                                 std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_var = namer_.Variable(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_var = namer_.Variable(struct_def);
 
     code += GenIndents(2) + "if not " + struct_var + "." + field_method +
             "IsNone():";
@@ -1160,9 +1164,9 @@
                                       std::string *code_ptr,
                                       int indents) const {
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_var = namer_.Variable(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_var = namer_.Variable(struct_def);
 
     code += GenIndents(indents) + "self." + field_field + " = []";
     code += GenIndents(indents) + "for i in range(" + struct_var + "." +
@@ -1175,9 +1179,9 @@
                                 const FieldDef &field,
                                 std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_var = namer_.Variable(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_var = namer_.Variable(struct_def);
 
     code += GenIndents(2) + "if not " + struct_var + "." + field_method +
             "IsNone():";
@@ -1200,9 +1204,9 @@
   void GenUnPackForScalar(const StructDef &struct_def, const FieldDef &field,
                           std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_var = namer_.Variable(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_var = namer_.Variable(struct_def);
 
     code += GenIndents(2) + "self." + field_field + " = " + struct_var + "." +
             field_method + "()";
@@ -1248,7 +1252,7 @@
 
     // Writes import statements and code into the generated file.
     auto &code_base = *code_ptr;
-    const auto struct_var = namer_.Variable(struct_def.name);
+    const auto struct_var = namer_.Variable(struct_def);
 
     GenReceiverForObjectAPI(struct_def, code_ptr);
     code_base += "_UnPack(self, " + struct_var + "):";
@@ -1269,7 +1273,7 @@
   void GenPackForStruct(const StructDef &struct_def,
                         std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto struct_fn = namer_.Function(struct_def.name);
+    const auto struct_fn = namer_.Function(struct_def);
 
     GenReceiverForObjectAPI(struct_def, code_ptr);
     code += "Pack(self, builder):";
@@ -1289,9 +1293,9 @@
                                    std::string *code_ptr) const {
     auto &code_prefix = *code_prefix_ptr;
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto struct_type = namer_.Type(struct_def.name);
-    const auto field_method = namer_.Method(field.name);
+    const auto field_field = namer_.Field(field);
+    const auto struct_type = namer_.Type(struct_def);
+    const auto field_method = namer_.Method(field);
 
     // Creates the field.
     code_prefix += GenIndents(2) + "if self." + field_field + " is not None:";
@@ -1332,9 +1336,9 @@
                                          std::string *code_ptr,
                                          int indents) const {
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_type = namer_.Type(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_type = namer_.Type(struct_def);
     const auto vectortype = field.value.type.VectorType();
 
     code += GenIndents(indents) + struct_type + "Start" + field_method +
@@ -1368,9 +1372,9 @@
                                    std::string *code_ptr) const {
     auto &code = *code_ptr;
     auto &code_prefix = *code_prefix_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_type = namer_.Type(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_type = namer_.Type(struct_def);
 
     // Adds the field into the struct.
     code += GenIndents(2) + "if self." + field_field + " is not None:";
@@ -1411,9 +1415,9 @@
                              std::string *code_ptr) const {
     auto &code_prefix = *code_prefix_ptr;
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_type = namer_.Type(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_type = namer_.Type(struct_def);
 
     if (field.value.type.struct_def->fixed) {
       // Pure struct fields need to be created along with their parent
@@ -1438,9 +1442,9 @@
                             std::string *code_ptr) const {
     auto &code_prefix = *code_prefix_ptr;
     auto &code = *code_ptr;
-    const auto field_field = namer_.Field(field.name);
-    const auto field_method = namer_.Method(field.name);
-    const auto struct_type = namer_.Type(struct_def.name);
+    const auto field_field = namer_.Field(field);
+    const auto field_method = namer_.Method(field);
+    const auto struct_type = namer_.Type(struct_def);
 
     // TODO(luwa): TypeT should be moved under the None check as well.
     code_prefix += GenIndents(2) + "if self." + field_field + " is not None:";
@@ -1455,8 +1459,8 @@
                        std::string *code_ptr) const {
     auto &code_base = *code_ptr;
     std::string code, code_prefix;
-    const auto struct_var = namer_.Variable(struct_def.name);
-    const auto struct_type = namer_.Type(struct_def.name);
+    const auto struct_var = namer_.Variable(struct_def);
+    const auto struct_type = namer_.Type(struct_def);
 
     GenReceiverForObjectAPI(struct_def, code_ptr);
     code_base += "Pack(self, builder):";
@@ -1466,8 +1470,8 @@
       auto &field = **it;
       if (field.deprecated) continue;
 
-      const auto field_method = namer_.Method(field.name);
-      const auto field_field = namer_.Field(field.name);
+      const auto field_method = namer_.Method(field);
+      const auto field_field = namer_.Field(field);
 
       switch (field.value.type.base_type) {
         case BASE_TYPE_STRUCT: {
@@ -1555,12 +1559,12 @@
   void GenUnionCreatorForStruct(const EnumDef &enum_def, const EnumVal &ev,
                                 std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto union_type = namer_.Type(enum_def.name);
-    const auto variant = namer_.Variant(ev.name);
-    auto field_type = namer_.ObjectType(GenTypeGet(ev.union_type));
+    const auto union_type = namer_.Type(enum_def);
+    const auto variant = namer_.Variant(ev);
+    auto field_type = namer_.ObjectType(*ev.union_type.struct_def);
 
-    code += GenIndents(1) + "if unionType == " + union_type + "()." +
-            variant + ":";
+    code +=
+        GenIndents(1) + "if unionType == " + union_type + "()." + variant + ":";
     if (parser_.opts.include_dependence_headers) {
       auto package_reference = GenPackageReference(ev.union_type);
       code += GenIndents(2) + "import " + package_reference;
@@ -1573,11 +1577,11 @@
   void GenUnionCreatorForString(const EnumDef &enum_def, const EnumVal &ev,
                                 std::string *code_ptr) const {
     auto &code = *code_ptr;
-    const auto union_type = namer_.Type(enum_def.name);
-    const auto variant = namer_.Variant(ev.name);
+    const auto union_type = namer_.Type(enum_def);
+    const auto variant = namer_.Variant(ev);
 
-    code += GenIndents(1) + "if unionType == " + union_type + "()." +
-            variant + ":";
+    code +=
+        GenIndents(1) + "if unionType == " + union_type + "()." + variant + ":";
     code += GenIndents(2) + "tab = Table(table.Bytes, table.Pos)";
     code += GenIndents(2) + "union = tab.String(table.Pos)";
     code += GenIndents(2) + "return union";
@@ -1588,7 +1592,7 @@
     if (enum_def.generated) return;
 
     auto &code = *code_ptr;
-    const auto enum_fn = namer_.Function(enum_def.name);
+    const auto enum_fn = namer_.Function(enum_def);
 
     code += "\n";
     code += "def " + enum_fn + "Creator(unionType, table):";
@@ -1721,7 +1725,7 @@
       if (parser_.opts.one_file && !enumcode.empty()) {
         *one_file_code += enumcode + "\n\n";
       } else {
-        if (!SaveType(namer_.File(enum_def.name, SkipFile::Suffix),
+        if (!SaveType(namer_.File(enum_def, SkipFile::Suffix),
                       *enum_def.defined_namespace, enumcode, false))
           return false;
       }
@@ -1742,7 +1746,7 @@
       if (parser_.opts.one_file && !declcode.empty()) {
         *one_file_code += declcode + "\n\n";
       } else {
-        if (!SaveType(namer_.File(struct_def.name, SkipFile::Suffix),
+        if (!SaveType(namer_.File(struct_def, SkipFile::Suffix),
                       *struct_def.defined_namespace, declcode, true))
           return false;
       }
@@ -1789,7 +1793,7 @@
 
  private:
   const SimpleFloatConstantGenerator float_const_gen_;
-  const Namer namer_;
+  const IdlNamer namer_;
 };
 
 }  // namespace python
diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp
index 17853a0..a5ce2f7 100644
--- a/src/idl_gen_rust.cpp
+++ b/src/idl_gen_rust.cpp
@@ -20,11 +20,12 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
-#include "namer.h"
+#include "idl_namer.h"
 
 namespace flatbuffers {
+namespace {
 
-Namer::Config RustDefaultConfig() {
+static Namer::Config RustDefaultConfig() {
   // Historical note: We've been using "keep" casing since the original
   // implementation, presumably because Flatbuffers schema style and Rust style
   // roughly align. We are not going to enforce proper casing since its an
@@ -37,6 +38,7 @@
            /*variables=*/Case::kUnknown,  // Unused.
            /*variants=*/Case::kKeep,
            /*enum_variant_seperator=*/"::",
+           /*escape_keywords=*/Namer::Config::Escape::BeforeConvertingCase,
            /*namespaces=*/Case::kSnake,
            /*namespace_seperator=*/"::",
            /*object_prefix=*/"",
@@ -50,7 +52,7 @@
            /*filename_extension=*/".rs" };
 }
 
-std::set<std::string> RustKeywords() {
+static std::set<std::string> RustKeywords() {
   return {
     // https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html
     "as",
@@ -172,7 +174,7 @@
 };
 
 // Convert a Type to a FullType (exhaustive).
-FullType GetFullType(const Type &type) {
+static FullType GetFullType(const Type &type) {
   // N.B. The order of these conditionals matters for some types.
 
   if (IsString(type)) {
@@ -262,15 +264,16 @@
   return ftBool;
 }
 
-bool IsBitFlagsEnum(const EnumDef &enum_def) {
+static bool IsBitFlagsEnum(const EnumDef &enum_def) {
   return enum_def.attributes.Lookup("bit_flags") != nullptr;
 }
 
 // TableArgs make required non-scalars "Option<_>".
 // TODO(cneo): Rework how we do defaults and stuff.
-bool IsOptionalToBuilder(const FieldDef &field) {
+static bool IsOptionalToBuilder(const FieldDef &field) {
   return field.IsOptional() || !IsScalar(field.value.type.base_type);
 }
+} // namespace
 
 bool GenerateRustModuleRootFile(const Parser &parser,
                                 const std::string &output_dir) {
@@ -279,7 +282,7 @@
     // so return true.
     return true;
   }
-  Namer namer(RustDefaultConfig().WithFlagOptions(parser.opts, output_dir),
+  Namer namer(WithFlagOptions(RustDefaultConfig(), parser.opts, output_dir),
               RustKeywords());
   // We gather the symbols into a tree of namespaces (which are rust mods) and
   // generate a file that gathers them all.
@@ -329,6 +332,7 @@
   code +=
       "// Automatically generated by the Flatbuffers compiler. "
       "Do not modify.";
+  code += "// @generated";
   root_module.GenerateImports(code);
   const bool success =
       SaveFile((output_dir + "mod.rs").c_str(), code.ToString(), false);
@@ -344,8 +348,8 @@
                 const std::string &file_name)
       : BaseGenerator(parser, path, file_name, "", "::", "rs"),
         cur_name_space_(nullptr),
-        namer_({ RustDefaultConfig().WithFlagOptions(parser.opts, path),
-                 RustKeywords() }) {
+        namer_(WithFlagOptions(RustDefaultConfig(), parser.opts, path),
+               RustKeywords()) {
     // TODO: Namer flag overrides should be in flatc or flatc_main.
     code_.SetPadding("  ");
   }
@@ -366,9 +370,14 @@
       if (symbol.generated) continue;
       code_.Clear();
       code_ += "// " + std::string(FlatBuffersGeneratedWarning());
+      code_ += "// @generated";
+      code_ += "extern crate alloc;";
       code_ += "extern crate flatbuffers;";
-      code_ += "use std::mem;";
-      code_ += "use std::cmp::Ordering;";
+      code_ += "use alloc::boxed::Box;";
+      code_ += "use alloc::string::{String, ToString};";
+      code_ += "use alloc::vec::Vec;";
+      code_ += "use core::mem;";
+      code_ += "use core::cmp::Ordering;";
       if (parser_.opts.rust_serialize) {
         code_ += "extern crate serde;";
         code_ +=
@@ -380,9 +389,9 @@
       gen_symbol(symbol);
 
       const std::string directories =
-          namer_.Directories(symbol.defined_namespace->components);
+          namer_.Directories(*symbol.defined_namespace);
       EnsureDirExists(directories);
-      const std::string file_path = directories + namer_.File(symbol.name);
+      const std::string file_path = directories + namer_.File(symbol);
       const bool save_success =
           SaveFile(file_path.c_str(), code_.ToString(), /*binary=*/false);
       if (!save_success) return false;
@@ -418,6 +427,7 @@
   bool GenerateOneFile() {
     code_.Clear();
     code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
+    code_ += "// @generated";
 
     assert(!cur_name_space_);
 
@@ -526,8 +536,11 @@
     return false;
   }
 
-  std::string NamespacedNativeName(const Definition &def) {
-    return WrapInNameSpace(def.defined_namespace, namer_.ObjectType(def.name));
+  std::string NamespacedNativeName(const EnumDef &def) {
+    return WrapInNameSpace(def.defined_namespace, namer_.ObjectType(def));
+  }
+  std::string NamespacedNativeName(const StructDef &def) {
+    return WrapInNameSpace(def.defined_namespace, namer_.ObjectType(def));
   }
 
   std::string WrapInNameSpace(const Definition &def) const {
@@ -663,7 +676,7 @@
 
   std::string GetEnumValue(const EnumDef &enum_def,
                            const EnumVal &enum_val) const {
-    return namer_.EnumVariant(enum_def.name, enum_val.name);
+    return namer_.EnumVariant(enum_def, enum_val);
   }
 
   // 1 suffix since old C++ can't figure out the overload.
@@ -671,7 +684,7 @@
                          std::function<void(const EnumVal &)> cb) {
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       const auto &ev = **it;
-      code_.SetValue("VARIANT", namer_.Variant(ev.name));
+      code_.SetValue("VARIANT", namer_.Variant(ev));
       code_.SetValue("VALUE", enum_def.ToString(ev));
       code_.IncrementIdentLevel();
       cb(ev);
@@ -690,7 +703,10 @@
   // an enum match function,
   // and an enum array of values
   void GenEnum(const EnumDef &enum_def) {
-    code_.SetValue("ENUM_TY", namer_.Type(enum_def.name));
+    const bool is_private = parser_.opts.no_leak_private_annotations &&
+        (enum_def.attributes.Lookup("private") != nullptr);
+    code_.SetValue("ACCESS_TYPE", is_private ? "pub(crate)" : "pub");
+    code_.SetValue("ENUM_TY", namer_.Type(enum_def));
     code_.SetValue("BASE_TYPE", GetEnumTypeForDecl(enum_def.underlying_type));
     code_.SetValue("ENUM_NAMESPACE", namer_.Namespace(enum_def.name));
     code_.SetValue("ENUM_CONSTANT", namer_.Constant(enum_def.name));
@@ -710,7 +726,7 @@
       code_ += "  flatbuffers::bitflags::bitflags! {";
       GenComment(enum_def.doc_comment, "    ");
       code_ += "    #[derive(Default)]";
-      code_ += "    pub struct {{ENUM_TY}}: {{BASE_TYPE}} {";
+      code_ += "    {{ACCESS_TYPE}} struct {{ENUM_TY}}: {{BASE_TYPE}} {";
       ForAllEnumValues1(enum_def, [&](const EnumVal &ev) {
         this->GenComment(ev.doc_comment, "    ");
         code_ += "    const {{VARIANT}} = {{VALUE}};";
@@ -743,7 +759,7 @@
       code_ += "pub const ENUM_VALUES_{{ENUM_CONSTANT}}: [{{ENUM_TY}}; " +
                num_fields + "] = [";
       ForAllEnumValues1(enum_def, [&](const EnumVal &ev) {
-        code_ += namer_.EnumVariant(enum_def.name, ev.name) + ",";
+        code_ += namer_.EnumVariant(enum_def, ev) + ",";
       });
       code_ += "];";
       code_ += "";
@@ -756,7 +772,7 @@
           "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, "
           "Default)]";
       code_ += "#[repr(transparent)]";
-      code_ += "pub struct {{ENUM_TY}}(pub {{BASE_TYPE}});";
+      code_ += "{{ACCESS_TYPE}} struct {{ENUM_TY}}(pub {{BASE_TYPE}});";
       code_ += "#[allow(non_upper_case_globals)]";
       code_ += "impl {{ENUM_TY}} {";
       ForAllEnumValues1(enum_def, [&](const EnumVal &ev) {
@@ -782,10 +798,10 @@
       code_ += "}";
 
       // Generate Debug. Unknown variants are printed like "<UNKNOWN 42>".
-      code_ += "impl std::fmt::Debug for {{ENUM_TY}} {";
+      code_ += "impl core::fmt::Debug for {{ENUM_TY}} {";
       code_ +=
-          "  fn fmt(&self, f: &mut std::fmt::Formatter) ->"
-          " std::fmt::Result {";
+          "  fn fmt(&self, f: &mut core::fmt::Formatter) ->"
+          " core::fmt::Result {";
       code_ += "    if let Some(name) = self.variant_name() {";
       code_ += "      f.write_str(name)";
       code_ += "    } else {";
@@ -872,8 +888,8 @@
 
     if (enum_def.is_union) {
       // Generate typesafe offset(s) for unions
-      code_.SetValue("UNION_TYPE", namer_.Type(enum_def.name));
-      code_ += "pub struct {{UNION_TYPE}}UnionTableOffset {}";
+      code_.SetValue("UNION_TYPE", namer_.Type(enum_def));
+      code_ += "{{ACCESS_TYPE}} struct {{UNION_TYPE}}UnionTableOffset {}";
       code_ += "";
       if (parser_.opts.generate_object_based_api) { GenUnionObject(enum_def); }
     }
@@ -885,13 +901,12 @@
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       auto &enum_val = **it;
       if (enum_val.union_type.base_type == BASE_TYPE_NONE) continue;
-      code_.SetValue("VARIANT_NAME", namer_.Variant(enum_val.name));
+      code_.SetValue("VARIANT_NAME", namer_.Variant(enum_val));
       // For legacy reasons, enum variants are Keep case while enum native
       // variants are UpperCamel case.
-      code_.SetValue(
-          "NATIVE_VARIANT",
-          ConvertCase(namer_.EscapeKeyword(enum_val.name), Case::kUpperCamel));
-      code_.SetValue("U_ELEMENT_NAME", namer_.Method(enum_val.name));
+      code_.SetValue("NATIVE_VARIANT",
+                     namer_.LegacyRustNativeVariant(enum_val));
+      code_.SetValue("U_ELEMENT_NAME", namer_.Method(enum_val));
       code_.SetValue("U_ELEMENT_TABLE_TYPE",
                      NamespacedNativeName(*enum_val.union_type.struct_def));
       code_.IncrementIdentLevel();
@@ -900,16 +915,16 @@
     }
   }
   void GenUnionObject(const EnumDef &enum_def) {
-    code_.SetValue("ENUM_TY", namer_.Type(enum_def.name));
-    code_.SetValue("ENUM_FN", namer_.Function(enum_def.name));
-    code_.SetValue("ENUM_OTY", namer_.ObjectType(enum_def.name));
+    code_.SetValue("ENUM_TY", namer_.Type(enum_def));
+    code_.SetValue("ENUM_FN", namer_.Function(enum_def));
+    code_.SetValue("ENUM_OTY", namer_.ObjectType(enum_def));
 
     // Generate native union.
     code_ += "#[allow(clippy::upper_case_acronyms)]";  // NONE's spelling is
                                                        // intended.
     code_ += "#[non_exhaustive]";
     code_ += "#[derive(Debug, Clone, PartialEq)]";
-    code_ += "pub enum {{ENUM_OTY}} {";
+    code_ += "{{ACCESS_TYPE}} enum {{ENUM_OTY}} {";
     code_ += "  NONE,";
     ForAllUnionObjectVariantsBesidesNone(enum_def, [&] {
       code_ += "{{NATIVE_VARIANT}}(Box<{{U_ELEMENT_TABLE_TYPE}}>),";
@@ -961,7 +976,7 @@
           "pub fn take_{{U_ELEMENT_NAME}}(&mut self) -> "
           "Option<Box<{{U_ELEMENT_TABLE_TYPE}}>> {";
       code_ += "  if let Self::{{NATIVE_VARIANT}}(_) = self {";
-      code_ += "    let v = std::mem::replace(self, Self::NONE);";
+      code_ += "    let v = core::mem::replace(self, Self::NONE);";
       code_ += "    if let Self::{{NATIVE_VARIANT}}(w) = v {";
       code_ += "      Some(w)";
       code_ += "    } else {";
@@ -997,13 +1012,6 @@
     code_ += "}";  // End union methods impl.
   }
 
-  std::string GetFieldOffsetName(const FieldDef &field) {
-    // FIXME: VT_FIELD_NAME is not screaming snake case by legacy mistake.
-    // but changing this is probably a breaking change.
-    return "VT_" +
-           ConvertCase(namer_.EscapeKeyword(field.name), Case::kAllUpper);
-  }
-
   enum DefaultContext { kBuilder, kAccessor, kObject };
   std::string GetDefaultValue(const FieldDef &field,
                               const DefaultContext context) {
@@ -1033,7 +1041,7 @@
         if (!ev) return "Default::default()";  // Bitflags enum.
         return WrapInNameSpace(
             field.value.type.enum_def->defined_namespace,
-            namer_.EnumVariant(field.value.type.enum_def->name, ev->name));
+            namer_.EnumVariant(*field.value.type.enum_def, *ev));
       }
       case ftUnionValue: {
         return ObjectFieldType(field, true) + "::NONE";
@@ -1539,7 +1547,7 @@
 
   std::string GenTableAccessorFuncBody(const FieldDef &field,
                                        const std::string &lifetime) {
-    const std::string vt_offset = GetFieldOffsetName(field);
+    const std::string vt_offset = namer_.LegacyRustFieldOffsetName(field);
     const std::string typname = FollowType(field.value.type, lifetime);
     // Default-y fields (scalars so far) are neither optional nor required.
     const std::string default_value =
@@ -1582,9 +1590,9 @@
       const EnumVal &ev = **it;
       // TODO(cneo): Can variants be deprecated, should we skip them?
       if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
-      code_.SetValue("U_ELEMENT_ENUM_TYPE",
-                     WrapInNameSpace(def.defined_namespace,
-                                     namer_.EnumVariant(def.name, ev.name)));
+      code_.SetValue(
+          "U_ELEMENT_ENUM_TYPE",
+          WrapInNameSpace(def.defined_namespace, namer_.EnumVariant(def, ev)));
       code_.SetValue(
           "U_ELEMENT_TABLE_TYPE",
           WrapInNameSpace(ev.union_type.struct_def->defined_namespace,
@@ -1601,11 +1609,11 @@
     // diff when refactoring to the `ForAllX` helper functions.
     auto go = [&](const FieldDef &field) {
       if (field.deprecated) return;
-      code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
+      code_.SetValue("OFFSET_NAME", namer_.LegacyRustFieldOffsetName(field));
       code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset));
-      code_.SetValue("FIELD", namer_.Field(field.name));
+      code_.SetValue("FIELD", namer_.Field(field));
       code_.SetValue("BLDR_DEF_VAL", GetDefaultValue(field, kBuilder));
-      code_.SetValue("DISCRIMINANT", namer_.Method(field.name) + "_type");
+      code_.SetValue("DISCRIMINANT", namer_.Field(field) + "_type");
       code_.IncrementIdentLevel();
       cb(field);
       code_.DecrementIdentLevel();
@@ -1620,18 +1628,22 @@
   // Generate an accessor struct, builder struct, and create function for a
   // table.
   void GenTable(const StructDef &struct_def) {
-    code_.SetValue("STRUCT_TY", namer_.Type(struct_def.name));
-    code_.SetValue("STRUCT_FN", namer_.Function(struct_def.name));
+
+    const bool is_private = parser_.opts.no_leak_private_annotations &&
+        (struct_def.attributes.Lookup("private") != nullptr);
+    code_.SetValue("ACCESS_TYPE", is_private ? "pub(crate)" : "pub");
+    code_.SetValue("STRUCT_TY", namer_.Type(struct_def));
+    code_.SetValue("STRUCT_FN", namer_.Function(struct_def));
 
     // Generate an offset type, the base type, the Follow impl, and the
     // init_from_table impl.
-    code_ += "pub enum {{STRUCT_TY}}Offset {}";
+    code_ += "{{ACCESS_TYPE}} enum {{STRUCT_TY}}Offset {}";
     code_ += "#[derive(Copy, Clone, PartialEq)]";
     code_ += "";
 
     GenComment(struct_def.doc_comment);
 
-    code_ += "pub struct {{STRUCT_TY}}<'a> {";
+    code_ += "{{ACCESS_TYPE}} struct {{STRUCT_TY}}<'a> {";
     code_ += "  pub _tab: flatbuffers::Table<'a>,";
     code_ += "}";
     code_ += "";
@@ -1702,7 +1714,7 @@
     if (parser_.opts.generate_object_based_api) {
       // TODO(cneo): Replace more for loops with ForAllX stuff.
       // TODO(cneo): Manage indentation with IncrementIdentLevel?
-      code_.SetValue("STRUCT_OTY", namer_.ObjectType(struct_def.name));
+      code_.SetValue("STRUCT_OTY", namer_.ObjectType(struct_def));
       code_ += "  pub fn unpack(&self) -> {{STRUCT_OTY}} {";
       ForAllObjectTableFields(struct_def, [&](const FieldDef &field) {
         const Type &type = field.value.type;
@@ -1939,7 +1951,7 @@
       const EnumDef &union_def = *field.value.type.enum_def;
       code_.SetValue("UNION_TYPE", WrapInNameSpace(union_def));
       code_.SetValue("UNION_TYPE_OFFSET_NAME",
-                     GetFieldOffsetName(field) + "_TYPE");
+                     namer_.LegacyRustFieldOffsetName(field) + "_TYPE");
       code_ +=
           "\n     .visit_union::<{{UNION_TYPE}}, _>("
           "\"{{FIELD}}_type\", Self::{{UNION_TYPE_OFFSET_NAME}}, "
@@ -1965,7 +1977,7 @@
     // Generate an args struct:
     code_.SetValue("MAYBE_LT",
                    TableBuilderArgsNeedsLifetime(struct_def) ? "<'a>" : "");
-    code_ += "pub struct {{STRUCT_TY}}Args{{MAYBE_LT}} {";
+    code_ += "{{ACCESS_TYPE}} struct {{STRUCT_TY}}Args{{MAYBE_LT}} {";
     ForAllTableFields(struct_def, [&](const FieldDef &field) {
       code_.SetValue("PARAM_TYPE", TableBuilderArgsDefnType(field, "'a"));
       code_ += "  pub {{FIELD}}: {{PARAM_TYPE}},";
@@ -2010,12 +2022,12 @@
           if (type.base_type == BASE_TYPE_UNION) {
             const auto &enum_def = *type.enum_def;
             code_.SetValue("ENUM_TY", WrapInNameSpace(enum_def));
-            code_.SetValue("FIELD", namer_.Field(field.name));
+            code_.SetValue("FIELD", namer_.Field(field));
 
             code_ += "    match self.{{FIELD}}_type() {";
             code_ += "      {{ENUM_TY}}::NONE => (),";
             ForAllUnionObjectVariantsBesidesNone(enum_def, [&] {
-              code_.SetValue("FIELD", namer_.Field(field.name));
+              code_.SetValue("FIELD", namer_.Field(field));
               code_ += "      {{ENUM_TY}}::{{VARIANT_NAME}} => {";
               code_ +=
                   "        let f = "
@@ -2054,7 +2066,7 @@
     }
 
     // Generate a builder struct:
-    code_ += "pub struct {{STRUCT_TY}}Builder<'a: 'b, 'b> {";
+    code_ += "{{ACCESS_TYPE}} struct {{STRUCT_TY}}Builder<'a: 'b, 'b> {";
     code_ += "  fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
     code_ +=
         "  start_: flatbuffers::WIPOffset<"
@@ -2065,7 +2077,7 @@
     code_ += "impl<'a: 'b, 'b> {{STRUCT_TY}}Builder<'a, 'b> {";
     ForAllTableFields(struct_def, [&](const FieldDef &field) {
       const bool is_scalar = IsScalar(field.value.type.base_type);
-      std::string offset = GetFieldOffsetName(field);
+      std::string offset = namer_.LegacyRustFieldOffsetName(field);
       // Generate functions to add data, which take one of two forms.
       //
       // If a value has a default:
@@ -2077,8 +2089,7 @@
       //   fn add_x(x_: type) {
       //     fbb_.push_slot_always::<type>(offset, x_);
       //   }
-      code_.SetValue("FIELD_OFFSET",
-                     namer_.Type(struct_def.name) + "::" + offset);
+      code_.SetValue("FIELD_OFFSET", namer_.Type(struct_def) + "::" + offset);
       code_.SetValue("FIELD_TYPE", TableBuilderArgsAddFuncType(field, "'b "));
       code_.SetValue("FUNC_BODY", TableBuilderArgsAddFuncBody(field));
       code_ += "#[inline]";
@@ -2126,10 +2137,10 @@
     code_ += "}";
     code_ += "";
 
-    code_ += "impl std::fmt::Debug for {{STRUCT_TY}}<'_> {";
+    code_ += "impl core::fmt::Debug for {{STRUCT_TY}}<'_> {";
     code_ +=
-        "  fn fmt(&self, f: &mut std::fmt::Formatter<'_>"
-        ") -> std::fmt::Result {";
+        "  fn fmt(&self, f: &mut core::fmt::Formatter<'_>"
+        ") -> core::fmt::Result {";
     code_ += "    let mut ds = f.debug_struct(\"{{STRUCT_TY}}\");";
     ForAllTableFields(struct_def, [&](const FieldDef &field) {
       if (GetFullType(field.value.type) == ftUnionValue) {
@@ -2170,13 +2181,13 @@
   }
 
   void GenTableObject(const StructDef &table) {
-    code_.SetValue("STRUCT_OTY", namer_.ObjectType(table.name));
-    code_.SetValue("STRUCT_TY", namer_.Type(table.name));
+    code_.SetValue("STRUCT_OTY", namer_.ObjectType(table));
+    code_.SetValue("STRUCT_TY", namer_.Type(table));
 
     // Generate the native object.
     code_ += "#[non_exhaustive]";
     code_ += "#[derive(Debug, Clone, PartialEq)]";
-    code_ += "pub struct {{STRUCT_OTY}} {";
+    code_ += "{{ACCESS_TYPE}} struct {{STRUCT_OTY}} {";
     ForAllObjectTableFields(table, [&](const FieldDef &field) {
       // Union objects combine both the union discriminant and value, so we
       // skip making a field for the discriminant.
@@ -2221,7 +2232,7 @@
         case ftUnionKey: return;  // Generate union type with union value.
         case ftUnionValue: {
           code_.SetValue("ENUM_METHOD",
-                         namer_.Method(field.value.type.enum_def->name));
+                         namer_.Method(*field.value.type.enum_def));
           code_ +=
               "  let {{FIELD}}_type = "
               "self.{{FIELD}}.{{ENUM_METHOD}}_type();";
@@ -2309,7 +2320,7 @@
     for (auto it = v.begin(); it != v.end(); it++) {
       const FieldDef &field = **it;
       if (field.deprecated) continue;
-      code_.SetValue("FIELD", namer_.Field(field.name));
+      code_.SetValue("FIELD", namer_.Field(field));
       code_.SetValue("FIELD_OTY", ObjectFieldType(field, true));
       code_.IncrementIdentLevel();
       cb(field);
@@ -2349,7 +2360,7 @@
     code_ += "#[inline]";
     code_ +=
         "pub fn key_compare_with_value(&self, val: {{KEY_TYPE}}) -> "
-        "::std::cmp::Ordering {";
+        "::core::cmp::Ordering {";
     code_ += "  let key = self.{{FIELD}}();";
     code_ += "  key.cmp({{REF}}val)";
     code_ += "}";
@@ -2359,8 +2370,8 @@
   // must only be called if the root table is defined.
   void GenRootTableFuncs(const StructDef &struct_def) {
     FLATBUFFERS_ASSERT(parser_.root_struct_def_ && "root table not defined");
-    code_.SetValue("STRUCT_TY", namer_.Type(struct_def.name));
-    code_.SetValue("STRUCT_FN", namer_.Function(struct_def.name));
+    code_.SetValue("STRUCT_TY", namer_.Type(struct_def));
+    code_.SetValue("STRUCT_FN", namer_.Function(struct_def));
     code_.SetValue("STRUCT_CONST", namer_.Constant(struct_def.name));
 
     // The root datatype accessors:
@@ -2570,7 +2581,7 @@
       const auto &field = **it;
       code_.SetValue("FIELD_TYPE", GetTypeGet(field.value.type));
       code_.SetValue("FIELD_OTY", ObjectFieldType(field, false));
-      code_.SetValue("FIELD", namer_.Field(field.name));
+      code_.SetValue("FIELD", namer_.Field(field));
       code_.SetValue("FIELD_OFFSET", NumToString(offset_to_field));
       code_.SetValue(
           "REF",
@@ -2584,12 +2595,15 @@
   }
   // Generate an accessor struct with constructor for a flatbuffers struct.
   void GenStruct(const StructDef &struct_def) {
+    const bool is_private = parser_.opts.no_leak_private_annotations &&
+        (struct_def.attributes.Lookup("private") != nullptr);
+    code_.SetValue("ACCESS_TYPE", is_private ? "pub(crate)" : "pub");
     // Generates manual padding and alignment.
     // Variables are private because they contain little endian data on all
     // platforms.
     GenComment(struct_def.doc_comment);
     code_.SetValue("ALIGN", NumToString(struct_def.minalign));
-    code_.SetValue("STRUCT_TY", namer_.Type(struct_def.name));
+    code_.SetValue("STRUCT_TY", namer_.Type(struct_def));
     code_.SetValue("STRUCT_SIZE", NumToString(struct_def.bytesize));
 
     // We represent Flatbuffers-structs in Rust-u8-arrays since the data may be
@@ -2601,7 +2615,7 @@
     code_ += "// struct {{STRUCT_TY}}, aligned to {{ALIGN}}";
     code_ += "#[repr(transparent)]";
     code_ += "#[derive(Clone, Copy, PartialEq)]";
-    code_ += "pub struct {{STRUCT_TY}}(pub [u8; {{STRUCT_SIZE}}]);";
+    code_ += "{{ACCESS_TYPE}} struct {{STRUCT_TY}}(pub [u8; {{STRUCT_SIZE}}]);";
     code_ += "impl Default for {{STRUCT_TY}} { ";
     code_ += "  fn default() -> Self { ";
     code_ += "    Self([0; {{STRUCT_SIZE}}])";
@@ -2609,10 +2623,10 @@
     code_ += "}";
 
     // Debug for structs.
-    code_ += "impl std::fmt::Debug for {{STRUCT_TY}} {";
+    code_ += "impl core::fmt::Debug for {{STRUCT_TY}} {";
     code_ +=
-        "  fn fmt(&self, f: &mut std::fmt::Formatter"
-        ") -> std::fmt::Result {";
+        "  fn fmt(&self, f: &mut core::fmt::Formatter"
+        ") -> core::fmt::Result {";
     code_ += "    f.debug_struct(\"{{STRUCT_TY}}\")";
     ForAllStructFields(struct_def, [&](const FieldDef &unused) {
       (void)unused;
@@ -2648,7 +2662,7 @@
     code_ += "    fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
     code_ += "        let src = unsafe {";
     code_ +=
-        "            ::std::slice::from_raw_parts("
+        "            ::core::slice::from_raw_parts("
         "self as *const {{STRUCT_TY}} as *const u8, Self::size())";
     code_ += "        };";
     code_ += "        dst.copy_from_slice(src);";
@@ -2661,7 +2675,7 @@
     code_ += "    fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
     code_ += "        let src = unsafe {";
     code_ +=
-        "            ::std::slice::from_raw_parts("
+        "            ::core::slice::from_raw_parts("
         "*self as *const {{STRUCT_TY}} as *const u8, Self::size())";
     code_ += "        };";
     code_ += "        dst.copy_from_slice(src);";
@@ -2793,7 +2807,7 @@
                          NumToString(InlineSize(field.value.type)));
           code_ += "pub fn set_{{FIELD}}(&mut self, x: &{{FIELD_TYPE}}) {";
           code_ += "  unsafe {";
-          code_ += "    std::ptr::copy(";
+          code_ += "    core::ptr::copy(";
           code_ += "      x.as_ptr() as *const u8,";
           code_ += "      self.0.as_mut_ptr().add({{FIELD_OFFSET}}),";
           code_ += "      {{FIELD_SIZE}},";
@@ -2819,7 +2833,7 @@
 
     // Generate Object API unpack method.
     if (parser_.opts.generate_object_based_api) {
-      code_.SetValue("STRUCT_OTY", namer_.ObjectType(struct_def.name));
+      code_.SetValue("STRUCT_OTY", namer_.ObjectType(struct_def));
       code_ += "  pub fn unpack(&self) -> {{STRUCT_OTY}} {";
       code_ += "    {{STRUCT_OTY}} {";
       ForAllStructFields(struct_def, [&](const FieldDef &field) {
@@ -2848,7 +2862,7 @@
     if (parser_.opts.generate_object_based_api) {
       // Struct declaration
       code_ += "#[derive(Debug, Clone, PartialEq, Default)]";
-      code_ += "pub struct {{STRUCT_OTY}} {";
+      code_ += "{{ACCESS_TYPE}} struct {{STRUCT_OTY}} {";
       ForAllStructFields(struct_def, [&](const FieldDef &field) {
         (void)field;  // unused.
         code_ += "pub {{FIELD}}: {{FIELD_OTY}},";
@@ -2906,8 +2920,8 @@
         }
       }
     }
-    code_ += indent + "use std::mem;";
-    code_ += indent + "use std::cmp::Ordering;";
+    code_ += indent + "use core::mem;";
+    code_ += indent + "use core::cmp::Ordering;";
     code_ += "";
     if (parser_.opts.rust_serialize) {
       code_ += indent + "extern crate serde;";
@@ -2964,7 +2978,7 @@
   }
 
  private:
-  Namer namer_;
+  IdlNamer namer_;
 };
 
 }  // namespace rust
diff --git a/src/idl_gen_swift.cpp b/src/idl_gen_swift.cpp
index c3c322f..b3baa28 100644
--- a/src/idl_gen_swift.cpp
+++ b/src/idl_gen_swift.cpp
@@ -21,21 +21,133 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
+#include "idl_namer.h"
 
 namespace flatbuffers {
 
 namespace swift {
 
-inline std::string GenIndirect(const std::string &reading) {
+namespace {
+
+static Namer::Config SwiftDefaultConfig() {
+  return { /*types=*/Case::kKeep,
+           /*constants=*/Case::kLowerCamel,
+           /*methods=*/Case::kLowerCamel,
+           /*functions=*/Case::kLowerCamel,
+           /*fields=*/Case::kLowerCamel,
+           /*variables=*/Case::kLowerCamel,
+           /*variants=*/Case::kLowerCamel,
+           /*enum_variant_seperator=*/".",
+           /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
+           /*namespaces=*/Case::kKeep,
+           /*namespace_seperator=*/"_",
+           /*object_prefix=*/"",
+           /*object_suffix=*/"T",
+           /*keyword_prefix=*/"",
+           /*keyword_suffix=*/"_",
+           /*filenames=*/Case::kKeep,
+           /*directories=*/Case::kKeep,
+           /*output_path=*/"",
+           /*filename_suffix=*/"_generated",
+           /*filename_extension=*/".swift" };
+}
+
+static std::set<std::string> SwiftKeywords() {
+  return {
+    "associatedtype",
+    "class",
+    "deinit",
+    "enum",
+    "extension",
+    "fileprivate",
+    "func",
+    "import",
+    "init",
+    "inout",
+    "internal",
+    "let",
+    "open",
+    "operator",
+    "private",
+    "protocol",
+    "public",
+    "rethrows",
+    "static",
+    "struct",
+    "subscript",
+    "typealias",
+    "var",
+    "break",
+    "case",
+    "continue",
+    "default",
+    "defer",
+    "do",
+    "else",
+    "fallthrough",
+    "for",
+    "guard",
+    "if",
+    "in",
+    "repeat",
+    "return",
+    "switch",
+    "where",
+    "while",
+    "Any",
+    "catch",
+    "false",
+    "is",
+    "nil",
+    "super",
+    "self",
+    "Self",
+    "throw",
+    "throws",
+    "true",
+    "try",
+    "associativity",
+    "convenience",
+    "dynamic",
+    "didSet",
+    "final",
+    "get",
+    "infix",
+    "indirect",
+    "lazy",
+    "left",
+    "mutating",
+    "none",
+    "nonmutating",
+    "optional",
+    "override",
+    "postfix",
+    "precedence",
+    "prefix",
+    "Protocol",
+    "required",
+    "right",
+    "set",
+    "Type",
+    "unowned",
+    "weak",
+    "willSet",
+    "Void",
+  };
+}
+
+static std::string GenIndirect(const std::string &reading) {
   return "{{ACCESS}}.indirect(" + reading + ")";
 }
 
-inline std::string GenArrayMainBody(const std::string &optional) {
-  return "{{ACCESS_TYPE}} func {{VALUENAME}}(at index: Int32) -> "
+static std::string GenArrayMainBody(const std::string &optional) {
+  return "{{ACCESS_TYPE}} func {{FIELDMETHOD}}(at index: Int32) -> "
          "{{VALUETYPE}}" +
          optional + " { ";
 }
 
+} // namespace
+
 class SwiftGenerator : public BaseGenerator {
  private:
   CodeWriter code_;
@@ -45,92 +157,11 @@
  public:
   SwiftGenerator(const Parser &parser, const std::string &path,
                  const std::string &file_name)
-      : BaseGenerator(parser, path, file_name, "", "_", "swift") {
+      : BaseGenerator(parser, path, file_name, "", "_", "swift"),
+        namer_(WithFlagOptions(SwiftDefaultConfig(), parser.opts, path),
+               SwiftKeywords()) {
     namespace_depth = 0;
     code_.SetPadding("  ");
-    static const char *const keywords[] = {
-      "associatedtype",
-      "class",
-      "deinit",
-      "enum",
-      "extension",
-      "fileprivate",
-      "func",
-      "import",
-      "init",
-      "inout",
-      "internal",
-      "let",
-      "open",
-      "operator",
-      "private",
-      "protocol",
-      "public",
-      "rethrows",
-      "static",
-      "struct",
-      "subscript",
-      "typealias",
-      "var",
-      "break",
-      "case",
-      "continue",
-      "default",
-      "defer",
-      "do",
-      "else",
-      "fallthrough",
-      "for",
-      "guard",
-      "if",
-      "in",
-      "repeat",
-      "return",
-      "switch",
-      "where",
-      "while",
-      "Any",
-      "catch",
-      "false",
-      "is",
-      "nil",
-      "super",
-      "self",
-      "Self",
-      "throw",
-      "throws",
-      "true",
-      "try",
-      "associativity",
-      "convenience",
-      "dynamic",
-      "didSet",
-      "final",
-      "get",
-      "infix",
-      "indirect",
-      "lazy",
-      "left",
-      "mutating",
-      "none",
-      "nonmutating",
-      "optional",
-      "override",
-      "postfix",
-      "precedence",
-      "prefix",
-      "Protocol",
-      "required",
-      "right",
-      "set",
-      "Type",
-      "unowned",
-      "weak",
-      "willSet",
-      "Void",
-      nullptr,
-    };
-    for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
   }
 
   bool generate() {
@@ -140,9 +171,14 @@
     code_ += "// " + std::string(FlatBuffersGeneratedWarning());
     code_ += "// swiftlint:disable all";
     code_ += "// swiftformat:disable all\n";
-    code_ += "import FlatBuffers\n";
-    // Generate code for all the enum declarations.
+    if (parser_.opts.include_dependence_headers || parser_.opts.generate_all) {
+      if (parser_.opts.swift_implementation_only)
+        code_ += "@_implementationOnly \\";
 
+      code_ += "import FlatBuffers\n";
+    }
+
+    // Generate code for all the enum declarations.
     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
          ++it) {
       const auto &enum_def = **it;
@@ -183,10 +219,12 @@
 
   // Generates the reader for swift
   void GenStructReader(const StructDef &struct_def) {
-    auto is_private_access = struct_def.attributes.Lookup("private");
+    const bool is_private_access =
+        parser_.opts.swift_implementation_only ||
+        struct_def.attributes.Lookup("private") != nullptr;
     code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public");
     GenComment(struct_def.doc_comment);
-    code_.SetValue("STRUCTNAME", NameWrappedInNameSpace(struct_def));
+    code_.SetValue("STRUCTNAME", namer_.NamespacedType(struct_def));
     code_ +=
         "{{ACCESS_TYPE}} struct {{STRUCTNAME}}: NativeStruct, Verifiable, "
         "FlatbuffersInitializable\\";
@@ -203,14 +241,14 @@
 
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
 
       if (!constructor.empty()) constructor += ", ";
 
-      auto name = Name(field);
-      auto type = GenType(field.value.type);
-      code_.SetValue("VALUENAME", name);
+      const auto field_var = namer_.Variable(field);
+      const auto type = GenType(field.value.type);
+      code_.SetValue("FIELDVAR", field_var);
       if (IsEnum(field.value.type)) {
         code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false));
       }
@@ -218,18 +256,20 @@
       GenComment(field.doc_comment);
       std::string valueType =
           IsEnum(field.value.type) ? "{{BASEVALUE}}" : "{{VALUETYPE}}";
-      code_ += "private var _{{VALUENAME}}: " + valueType;
-      auto accessing_value = IsEnum(field.value.type) ? ".value" : "";
-      auto is_bool = IsBool(field.value.type.base_type);
-      auto base_value = IsStruct(field.value.type) ? (type + "()")
-        : is_bool ? ("0" == field.value.constant ? "false" : "true")
-        : field.value.constant;
+      code_ += "private var _{{FIELDVAR}}: " + valueType;
+      const auto accessing_value = IsEnum(field.value.type) ? ".value" : "";
+      const auto is_bool = IsBool(field.value.type.base_type);
+      const auto base_value =
+          IsStruct(field.value.type) ? (type + "()")
+          : is_bool ? ("0" == field.value.constant ? "false" : "true")
+                    : field.value.constant;
 
-      main_constructor.push_back("_" + name + " = " + name + accessing_value);
-      base_constructor.push_back("_" + name + " = " + base_value);
+      main_constructor.push_back("_" + field_var + " = " + field_var +
+                                 accessing_value);
+      base_constructor.push_back("_" + field_var + " = " + base_value);
 
       if (field.padding) { GenPadding(field, &padding_id); }
-      constructor += name + ": " + type;
+      constructor += field_var + ": " + type;
     }
     code_ += "";
     BuildStructConstructor(struct_def);
@@ -241,23 +281,22 @@
 
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
-      auto name = Name(field);
-      auto type = GenType(field.value.type);
-      code_.SetValue("VALUENAME", name);
-      code_.SetValue("VALUETYPE", type);
+      code_.SetValue("FIELDVAR", namer_.Variable(field));
+      code_.SetValue("VALUETYPE", GenType(field.value.type));
       GenComment(field.doc_comment);
       if (!IsEnum(field.value.type)) {
-        code_ += GenReaderMainBody() + "_{{VALUENAME}} }";
+        code_ += GenReaderMainBody() + "_{{FIELDVAR}} }";
       } else if (IsEnum(field.value.type)) {
         code_ +=
-            GenReaderMainBody() + "{{VALUETYPE}}(rawValue: _{{VALUENAME}})! }";
+            GenReaderMainBody() + "{{VALUETYPE}}(rawValue: _{{FIELDVAR}})! }";
       }
     }
     code_ += "";
     code_ +=
-        "public static func verify<T>(_ verifier: inout Verifier, at position: "
+        "{{ACCESS_TYPE}} static func verify<T>(_ verifier: inout Verifier, at "
+        "position: "
         "Int, of type: T.Type) throws where T: Verifiable {";
     Indent();
     code_ +=
@@ -275,22 +314,21 @@
     code_ += "let {{ACCESS}} = Struct(bb: bb, position: o)";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
-      auto name = Name(field);
-      auto type = field.value.type;
-      code_.SetValue("VALUENAME", name);
+      const auto type = field.value.type;
+      code_.SetValue("FIELDVAR", namer_.Variable(field));
       code_.SetValue("VALUETYPE", GenType(type));
       code_.SetValue("OFFSET", NumToString(field.value.offset));
       if (IsScalar(type.base_type)) {
         if (IsEnum(type))
           code_.SetValue("VALUETYPE", GenTypeBasic(field.value.type, false));
         code_ +=
-            "_{{VALUENAME}} = {{ACCESS}}.readBuffer(of: {{VALUETYPE}}.self, "
+            "_{{FIELDVAR}} = {{ACCESS}}.readBuffer(of: {{VALUETYPE}}.self, "
             "at: {{OFFSET}})";
       } else {
         code_ +=
-            "_{{VALUENAME}} = {{VALUETYPE}}({{ACCESS}}.bb, o: "
+            "_{{FIELDVAR}} = {{VALUETYPE}}({{ACCESS}}.bb, o: "
             "{{ACCESS}}.postion + {{OFFSET}})";
       }
     }
@@ -303,12 +341,11 @@
 
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
-      auto offset = NumToString(field.value.offset);
-      auto name = Name(field);
-      auto type = GenType(field.value.type);
-      code_.SetValue("VALUENAME", name);
+      const auto offset = NumToString(field.value.offset);
+      const auto type = GenType(field.value.type);
+      code_.SetValue("FIELDVAR", namer_.Variable(field));
       if (IsEnum(field.value.type)) {
         code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false));
       }
@@ -332,7 +369,7 @@
     }
 
     if (parser_.opts.generate_object_based_api) {
-      GenerateObjectAPIExtensionHeader(NameWrappedInNameSpace(struct_def));
+      GenerateObjectAPIExtensionHeader(namer_.NamespacedType(struct_def));
       code_ += "return builder.create(struct: obj)";
       Outdent();
       code_ += "}";
@@ -343,10 +380,12 @@
 
   // Generates the create function for swift
   void GenStructWriter(const StructDef &struct_def) {
-    auto is_private_access = struct_def.attributes.Lookup("private");
+    const bool is_private_access =
+        parser_.opts.swift_implementation_only ||
+        struct_def.attributes.Lookup("private") != nullptr;
     code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public");
-    code_.SetValue("STRUCTNAME", NameWrappedInNameSpace(struct_def));
-    code_.SetValue("SHORT_STRUCTNAME", Name(struct_def));
+    code_.SetValue("STRUCTNAME", namer_.NamespacedType(struct_def));
+    code_.SetValue("SHORT_STRUCTNAME", namer_.Method(struct_def));
     code_ += "extension {{STRUCTNAME}} {";
     Indent();
     code_ += "@discardableResult";
@@ -376,7 +415,7 @@
     auto &code = *code_ptr;
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
       const auto &field_type = field.value.type;
       if (IsStruct(field.value.type)) {
@@ -384,14 +423,15 @@
             *field_type.struct_def, code_ptr, (nameprefix + field.name),
             (object_name + "." + field.name), obj_api_named, is_obj_api);
       } else {
-        auto name = Name(field);
-        auto type = GenType(field.value.type);
+        const auto field_var = namer_.Variable(field);
+        const auto field_field = namer_.Field(field);
+        const auto type = GenType(field.value.type);
         if (!is_obj_api) {
-          code += nameprefix + name + ": " + type;
+          code += nameprefix + field_var + ": " + type;
           if (!IsEnum(field.value.type)) {
             code += " = ";
-            auto is_bool = IsBool(field.value.type.base_type);
-            auto constant =
+            const auto is_bool = IsBool(field.value.type.base_type);
+            const auto constant =
                 is_bool ? ("0" == field.value.constant ? "false" : "true")
                         : field.value.constant;
             code += constant;
@@ -399,8 +439,8 @@
           code += ", ";
           continue;
         }
-        code +=
-            nameprefix + name + ": " + obj_api_named + object_name + "." + name;
+        code += nameprefix + field_var + ": " + obj_api_named + object_name +
+                "." + field_field;
         code += ", ";
       }
     }
@@ -410,7 +450,9 @@
 
   // Generates the reader for swift
   void GenTable(const StructDef &struct_def) {
-    auto is_private_access = struct_def.attributes.Lookup("private");
+    const bool is_private_access =
+        parser_.opts.swift_implementation_only ||
+        struct_def.attributes.Lookup("private") != nullptr;
     code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public");
     GenObjectHeader(struct_def);
     GenTableAccessors(struct_def);
@@ -435,7 +477,7 @@
            it != struct_def.fields.vec.end(); ++it) {
         const auto &field = **it;
         if (field.deprecated) { continue; }
-        code_.SetValue("OFFSET_NAME", Name(field));
+        code_.SetValue("OFFSET_NAME", namer_.Variable(field));
         code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset));
         code_ += "case {{OFFSET_NAME}} = {{OFFSET_VALUE}}";
       }
@@ -450,8 +492,8 @@
   void GenObjectHeader(const StructDef &struct_def) {
     GenComment(struct_def.doc_comment);
 
-    code_.SetValue("SHORT_STRUCTNAME", Name(struct_def));
-    code_.SetValue("STRUCTNAME", NameWrappedInNameSpace(struct_def));
+    code_.SetValue("SHORT_STRUCTNAME", namer_.Type(struct_def));
+    code_.SetValue("STRUCTNAME", namer_.NamespacedType(struct_def));
     code_.SetValue("OBJECTTYPE", struct_def.fixed ? "Struct" : "Table");
     code_.SetValue("MUTABLE", struct_def.fixed ? Mutable() : "");
     code_ +=
@@ -468,12 +510,13 @@
     if (!struct_def.fixed) {
       if (parser_.file_identifier_.length()) {
         code_.SetValue("FILENAME", parser_.file_identifier_);
+        code_ += "{{ACCESS_TYPE}} static var id: String { \"{{FILENAME}}\" } ";
         code_ +=
             "{{ACCESS_TYPE}} static func finish(_ fbb: inout "
             "FlatBufferBuilder, end: "
             "Offset, prefix: Bool = false) { fbb.finish(offset: end, "
             "fileId: "
-            "\"{{FILENAME}}\", addPrefix: prefix) }";
+            "{{STRUCTNAME}}.id, addPrefix: prefix) }";
       }
       code_ +=
           "{{ACCESS_TYPE}} static func getRootAs{{SHORT_STRUCTNAME}}(bb: "
@@ -495,7 +538,7 @@
     std::vector<std::string> require_fields;
     std::vector<std::string> create_func_body;
     std::vector<std::string> create_func_header;
-    auto should_generate_create = struct_def.fields.vec.size() != 0;
+    const auto should_generate_create = struct_def.fields.vec.size() != 0;
 
     code_.SetValue("NUMBEROFFIELDS", NumToString(struct_def.fields.vec.size()));
     code_ +=
@@ -555,14 +598,12 @@
     std::string spacing = "";
 
     if (key_field != nullptr && !struct_def.fixed && struct_def.has_key) {
-      code_.SetValue("VALUENAME", NameWrappedInNameSpace(struct_def));
-      code_.SetValue("SHORT_VALUENAME", Name(struct_def));
       code_.SetValue("VOFFSET", NumToString(key_field->value.offset));
 
-      code_ +=
-          "{{ACCESS_TYPE}} static func "
-          "sortVectorOf{{SHORT_VALUENAME}}(offsets:[Offset], "
-          "_ fbb: inout FlatBufferBuilder) -> Offset {";
+      code_ += "{{ACCESS_TYPE}} static func " +
+               namer_.Method("sort_vector_of", struct_def) +
+               "(offsets:[Offset], "
+               "_ fbb: inout FlatBufferBuilder) -> Offset {";
       Indent();
       code_ += spacing + "var off = offsets";
       code_ +=
@@ -573,7 +614,7 @@
       code_ += spacing + "return fbb.createVector(ofOffsets: off)";
       Outdent();
       code_ += "}";
-      GenLookup(*key_field);
+      GenLookup(*key_field, namer_.NamespacedType(struct_def));
     }
   }
 
@@ -583,42 +624,44 @@
     std::string builder_string = ", _ fbb: inout FlatBufferBuilder) { ";
     auto &create_func_body = *create_body;
     auto &create_func_header = *create_header;
-    auto name = Name(field);
-    auto type = GenType(field.value.type);
-    auto opt_scalar =
+    const auto field_field = namer_.Field(field);
+    const auto field_var = namer_.Variable(field);
+    const auto type = GenType(field.value.type);
+    const auto opt_scalar =
         field.IsOptional() && IsScalar(field.value.type.base_type);
-    auto nullable_type = opt_scalar ? type + "?" : type;
-    code_.SetValue("VALUENAME", name);
+    const auto nullable_type = opt_scalar ? type + "?" : type;
+    code_.SetValue("FIELDVAR", namer_.Variable(field));
     code_.SetValue("VALUETYPE", nullable_type);
-    code_.SetValue("OFFSET", name);
+    code_.SetValue("OFFSET", namer_.Field(field));
     code_.SetValue("CONSTANT", field.value.constant);
     std::string check_if_vector =
         (IsVector(field.value.type) || IsArray(field.value.type)) ? "VectorOf("
                                                                   : "(";
-    auto body = "add" + check_if_vector + name + ": ";
+    const auto body = "add" + check_if_vector + field_field + ": ";
     code_ += "{{ACCESS_TYPE}} static func " + body + "\\";
 
-    create_func_body.push_back("{{STRUCTNAME}}." + body + name + ", &fbb)");
+    create_func_body.push_back("{{STRUCTNAME}}." + body + field_field +
+                               ", &fbb)");
 
     if (IsScalar(field.value.type.base_type) &&
         !IsBool(field.value.type.base_type)) {
-      std::string is_enum = IsEnum(field.value.type) ? ".rawValue" : "";
-      std::string optional_enum =
+      const std::string is_enum = IsEnum(field.value.type) ? ".rawValue" : "";
+      const std::string optional_enum =
           IsEnum(field.value.type) ? ("?" + is_enum) : "";
       code_ +=
-          "{{VALUETYPE}}" + builder_string + "fbb.add(element: {{VALUENAME}}\\";
+          "{{VALUETYPE}}" + builder_string + "fbb.add(element: {{FIELDVAR}}\\";
 
       code_ += field.IsOptional() ? (optional_enum + "\\")
                                   : (is_enum + ", def: {{CONSTANT}}\\");
 
       code_ += ", at: {{TABLEOFFSET}}.{{OFFSET}}.p) }";
 
-      auto default_value =
+      const auto default_value =
           IsEnum(field.value.type)
               ? (field.IsOptional() ? "nil" : GenEnumDefaultValue(field))
               : field.value.constant;
       create_func_header.push_back(
-          "" + name + ": " + nullable_type + " = " +
+          "" + field_field + ": " + nullable_type + " = " +
           (field.IsOptional() ? "nil" : default_value));
       return;
     }
@@ -629,48 +672,49 @@
 
       code_.SetValue("CONSTANT", default_value);
       code_.SetValue("VALUETYPE", field.IsOptional() ? "Bool?" : "Bool");
-      code_ += "{{VALUETYPE}}" + builder_string +
-               "fbb.add(element: {{VALUENAME}},\\";
+      code_ +=
+          "{{VALUETYPE}}" + builder_string + "fbb.add(element: {{FIELDVAR}},\\";
       code_ += field.IsOptional() ? "\\" : " def: {{CONSTANT}},";
       code_ += " at: {{TABLEOFFSET}}.{{OFFSET}}.p) }";
       create_func_header.push_back(
-          name + ": " + nullable_type + " = " +
+          field_var + ": " + nullable_type + " = " +
           (field.IsOptional() ? "nil" : default_value));
       return;
     }
 
     if (IsStruct(field.value.type)) {
-      auto create_struct =
-          "guard let {{VALUENAME}} = {{VALUENAME}} else { return };"
-          " fbb.create(struct: {{VALUENAME}}, position: "
+      const auto create_struct =
+          "guard let {{FIELDVAR}} = {{FIELDVAR}} else { return };"
+          " fbb.create(struct: {{FIELDVAR}}, position: "
           "{{TABLEOFFSET}}.{{OFFSET}}.p) }";
       code_ += type + "?" + builder_string + create_struct;
       /// Optional hard coded since structs are always optional
-      create_func_header.push_back(name + ": " + type + "? = nil");
+      create_func_header.push_back(field_var + ": " + type +
+                                   (field.IsOptional() ? "? = nil" : ""));
       return;
     }
 
-    auto camel_case_name =
-        ConvertCase(name, Case::kLowerCamel) +
+    const auto arg_label =
+        namer_.Variable(field) +
         (IsVector(field.value.type) || IsArray(field.value.type)
              ? "VectorOffset"
              : "Offset");
-    create_func_header.push_back(camel_case_name + " " + name + ": " +
-                                 "Offset = Offset()");
-    auto reader_type =
+    create_func_header.push_back(arg_label + " " + field_var + ": " + "Offset" +
+                                 (field.IsRequired() ? "" : " = Offset()"));
+    const auto reader_type =
         IsStruct(field.value.type) && field.value.type.struct_def->fixed
             ? "structOffset: {{TABLEOFFSET}}.{{OFFSET}}.p) }"
-            : "offset: {{VALUENAME}}, at: {{TABLEOFFSET}}.{{OFFSET}}.p) }";
+            : "offset: {{FIELDVAR}}, at: {{TABLEOFFSET}}.{{OFFSET}}.p) }";
     code_ += "Offset" + builder_string + "fbb.add(" + reader_type;
 
-    auto vectortype = field.value.type.VectorType();
+    const auto vectortype = field.value.type.VectorType();
 
     if ((vectortype.base_type == BASE_TYPE_STRUCT &&
          field.value.type.struct_def->fixed) &&
         (IsVector(field.value.type) || IsArray(field.value.type))) {
-      auto field_name = NameWrappedInNameSpace(*vectortype.struct_def);
-      code_ += "public static func startVectorOf" +
-               ConvertCase(name, Case::kUpperCamel) +
+      const auto field_name = namer_.NamespacedType(*vectortype.struct_def);
+      code_ += "{{ACCESS_TYPE}} static func " +
+               namer_.Method("start_vector_of", field_var) +
                "(_ size: Int, in builder: inout "
                "FlatBufferBuilder) {";
       Indent();
@@ -685,25 +729,26 @@
   void GenTableReader(const StructDef &struct_def) {
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
       GenTableReaderFields(field);
     }
   }
 
   void GenTableReaderFields(const FieldDef &field) {
-    auto offset = NumToString(field.value.offset);
-    auto name = Name(field);
-    auto type = GenType(field.value.type);
-    code_.SetValue("VALUENAME", name);
+    const auto offset = NumToString(field.value.offset);
+    const auto field_field = namer_.Field(field);
+    const auto type = GenType(field.value.type);
+    code_.SetValue("FIELDVAR", namer_.Variable(field));
+    code_.SetValue("FIELDMETHOD", namer_.Method(field));
     code_.SetValue("VALUETYPE", type);
-    code_.SetValue("OFFSET", name);
+    code_.SetValue("OFFSET", namer_.Constant(field.name));
     code_.SetValue("CONSTANT", field.value.constant);
     bool opt_scalar =
         field.IsOptional() && IsScalar(field.value.type.base_type);
     std::string def_Val = opt_scalar ? "nil" : "{{CONSTANT}}";
     std::string optional = opt_scalar ? "?" : "";
-    auto const_string = "return o == 0 ? " + def_Val + " : ";
+    const auto const_string = "return o == 0 ? " + def_Val + " : ";
     GenComment(field.doc_comment);
     if (IsScalar(field.value.type.base_type) && !IsEnum(field.value.type) &&
         !IsBool(field.value.type.base_type)) {
@@ -728,7 +773,7 @@
     }
 
     if (IsEnum(field.value.type)) {
-      auto default_value =
+      const auto default_value =
           field.IsOptional() ? "nil" : GenEnumDefaultValue(field);
       code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false));
       code_ += GenReaderMainBody(optional) + "\\";
@@ -739,16 +784,15 @@
       return;
     }
 
-    std::string is_required = field.IsRequired() ? "!" : "?";
-    auto required_reader = field.IsRequired() ? "return " : const_string;
+    const std::string is_required = field.IsRequired() ? "!" : "?";
+    const auto required_reader = field.IsRequired() ? "return " : const_string;
 
     if (IsStruct(field.value.type) && field.value.type.struct_def->fixed) {
       code_.SetValue("VALUETYPE", GenType(field.value.type));
       code_.SetValue("CONSTANT", "nil");
       code_ += GenReaderMainBody(is_required) + GenOffset() + required_reader +
                "{{ACCESS}}.readBuffer(of: {{VALUETYPE}}.self, at: o) }";
-      code_.SetValue("VALUENAME",
-                     "mutable" + ConvertCase(name, Case::kUpperCamel));
+      code_.SetValue("FIELDVAR", namer_.Variable("mutable", field_field));
       code_.SetValue("VALUETYPE", GenType(field.value.type) + Mutable());
       code_.SetValue("CONSTANT", "nil");
       code_ += GenReaderMainBody(is_required) + GenOffset() + required_reader +
@@ -765,12 +809,12 @@
         break;
 
       case BASE_TYPE_STRING: {
-        auto default_string = "\"" + field.value.constant + "\"";
+        const auto default_string = "\"" + field.value.constant + "\"";
         code_.SetValue("VALUETYPE", GenType(field.value.type));
         code_.SetValue("CONSTANT", field.IsDefault() ? default_string : "nil");
         code_ += GenReaderMainBody(is_required) + GenOffset() +
                  required_reader + "{{ACCESS}}.string(at: o) }";
-        code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}SegmentArray: [UInt8]" +
+        code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}SegmentArray: [UInt8]" +
                  is_required +
                  " { return "
                  "{{ACCESS}}.getVector(at: {{TABLEOFFSET}}.{{OFFSET}}.v) }";
@@ -781,7 +825,7 @@
       case BASE_TYPE_UNION:
         code_.SetValue("CONSTANT", "nil");
         code_ +=
-            "{{ACCESS_TYPE}} func {{VALUENAME}}<T: "
+            "{{ACCESS_TYPE}} func {{FIELDVAR}}<T: "
             "FlatbuffersInitializable>(type: "
             "T.Type) -> T" +
             is_required + " { " + GenOffset() + required_reader +
@@ -793,20 +837,22 @@
 
   void GenTableReaderVectorFields(const FieldDef &field) {
     std::string const_string = "return o == 0 ? {{CONSTANT}} : ";
-    auto vectortype = field.value.type.VectorType();
+    const auto vectortype = field.value.type.VectorType();
     code_.SetValue("SIZE", NumToString(InlineSize(vectortype)));
-    code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}Count: Int32 { " + GenOffset() +
+    code_.SetValue("HAS_FIELDVAR", namer_.Variable("has", field));
+    code_ += "{{ACCESS_TYPE}} var {{HAS_FIELDVAR}}: Bool { " + GenOffset() +
+             "return o == 0 ? false : true }";
+    code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}Count: Int32 { " + GenOffset() +
              "return o == 0 ? 0 : {{ACCESS}}.vector(count: o) }";
-    code_.SetValue("CONSTANT",
-                   IsScalar(vectortype.base_type) == true ? "0" : "nil");
-    auto nullable = IsScalar(vectortype.base_type) == true ? "" : "?";
-    nullable = IsEnum(vectortype) == true ? "?" : nullable;
+    code_.SetValue("CONSTANT", IsScalar(vectortype.base_type) ? "0" : "nil");
+    const auto nullable =
+        IsScalar(vectortype.base_type) && !IsEnum(vectortype) ? "" : "?";
 
     if (vectortype.base_type != BASE_TYPE_UNION) {
       code_ += GenArrayMainBody(nullable) + GenOffset() + "\\";
     } else {
       code_ +=
-          "{{ACCESS_TYPE}} func {{VALUENAME}}<T: FlatbuffersInitializable>(at "
+          "{{ACCESS_TYPE}} func {{FIELDVAR}}<T: FlatbuffersInitializable>(at "
           "index: "
           "Int32, type: T.Type) -> T? { " +
           GenOffset() + "\\";
@@ -825,7 +871,7 @@
           "{{ACCESS}}.directRead(of: {{VALUETYPE}}.self, offset: "
           "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }";
       code_ +=
-          "{{ACCESS_TYPE}} var {{VALUENAME}}: [{{VALUETYPE}}] { return "
+          "{{ACCESS_TYPE}} var {{FIELDVAR}}: [{{VALUETYPE}}] { return "
           "{{ACCESS}}.getVector(at: {{TABLEOFFSET}}.{{OFFSET}}.v) ?? [] }";
       if (parser_.opts.mutable_buffer) code_ += GenMutateArray();
       return;
@@ -836,8 +882,7 @@
       code_ +=
           "{{ACCESS}}.directRead(of: {{VALUETYPE}}.self, offset: "
           "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }";
-      code_.SetValue("VALUENAME",
-                     "mutable" + ConvertCase(Name(field), Case::kUpperCamel));
+      code_.SetValue("FIELDMETHOD", namer_.Method("mutable", field));
       code_.SetValue("VALUETYPE", GenType(field.value.type) + Mutable());
       code_ += GenArrayMainBody(nullable) + GenOffset() + const_string +
                GenConstructor("{{ACCESS}}.vector(at: o) + index * {{SIZE}}");
@@ -872,10 +917,10 @@
       code_ += GenConstructor(
           "{{ACCESS}}.indirect({{ACCESS}}.vector(at: o) + index * "
           "{{SIZE}})");
-      auto &sd = *field.value.type.struct_def;
-      auto &fields = sd.fields.vec;
+      const auto &sd = *field.value.type.struct_def;
+      const auto &fields = sd.fields.vec;
       for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
-        auto &key_field = **kit;
+        const auto &key_field = **kit;
         if (key_field.key) {
           GenByKeyFunctions(key_field);
           break;
@@ -889,13 +934,12 @@
     Indent();
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
-      auto name = Name(field);
 
       code_.SetValue("RAWVALUENAME", field.name);
-      code_.SetValue("VALUENAME", name);
-      code_ += "case {{VALUENAME}} = \"{{RAWVALUENAME}}\"";
+      code_.SetValue("FIELDVAR", namer_.Variable(field));
+      code_ += "case {{FIELDVAR}} = \"{{RAWVALUENAME}}\"";
     }
     Outdent();
     code_ += "}";
@@ -903,8 +947,8 @@
 
   void GenerateEncoderUnionBody(const FieldDef &field) {
     EnumDef &union_def = *field.value.type.enum_def;
-    auto is_vector = field.value.type.base_type == BASE_TYPE_VECTOR ||
-                     field.value.type.base_type == BASE_TYPE_ARRAY;
+    const auto is_vector = field.value.type.base_type == BASE_TYPE_VECTOR ||
+                           field.value.type.base_type == BASE_TYPE_ARRAY;
     if (field.value.type.base_type == BASE_TYPE_UTYPE ||
         (is_vector &&
          field.value.type.VectorType().base_type == BASE_TYPE_UTYPE))
@@ -912,28 +956,25 @@
     if (is_vector) {
       code_ +=
           "var enumsEncoder = container.nestedUnkeyedContainer(forKey: "
-          ".{{VALUENAME}}Type)";
+          ".{{FIELDVAR}}Type)";
       code_ +=
           "var contentEncoder = container.nestedUnkeyedContainer(forKey: "
-          ".{{VALUENAME}})";
-      code_ += "for index in 0..<{{VALUENAME}}Count {";
+          ".{{FIELDVAR}})";
+      code_ += "for index in 0..<{{FIELDVAR}}Count {";
       Indent();
-      code_ +=
-          "guard let type = {{VALUENAME}}Type(at: index) else { continue }";
+      code_ += "guard let type = {{FIELDVAR}}Type(at: index) else { continue }";
       code_ += "try enumsEncoder.encode(type)";
       code_ += "switch type {";
       for (auto it = union_def.Vals().begin(); it != union_def.Vals().end();
            ++it) {
         const auto &ev = **it;
-
-        auto name = Name(ev);
-        auto type = GenType(ev.union_type);
-        code_.SetValue("KEY", name);
+        const auto type = GenType(ev.union_type);
+        code_.SetValue("KEY", namer_.LegacySwiftVariant(ev));
         code_.SetValue("VALUETYPE", type);
         if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
         code_ += "case .{{KEY}}:";
         Indent();
-        code_ += "let _v = {{VALUENAME}}(at: index, type: {{VALUETYPE}}.self)";
+        code_ += "let _v = {{FIELDVAR}}(at: index, type: {{VALUETYPE}}.self)";
         code_ += "try contentEncoder.encode(_v)";
         Outdent();
       }
@@ -944,20 +985,18 @@
       return;
     }
 
-    code_ += "switch {{VALUENAME}}Type {";
+    code_ += "switch {{FIELDVAR}}Type {";
     for (auto it = union_def.Vals().begin(); it != union_def.Vals().end();
          ++it) {
       const auto &ev = **it;
-
-      auto name = Name(ev);
-      auto type = GenType(ev.union_type);
-      code_.SetValue("KEY", name);
+      const auto type = GenType(ev.union_type);
+      code_.SetValue("KEY", namer_.LegacySwiftVariant(ev));
       code_.SetValue("VALUETYPE", type);
       if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
       code_ += "case .{{KEY}}:";
       Indent();
-      code_ += "let _v = {{VALUENAME}}(type: {{VALUETYPE}}.self)";
-      code_ += "try container.encodeIfPresent(_v, forKey: .{{VALUENAME}})";
+      code_ += "let _v = {{FIELDVAR}}(type: {{VALUETYPE}}.self)";
+      code_ += "try container.encodeIfPresent(_v, forKey: .{{FIELDVAR}})";
       Outdent();
     }
     code_ += "default: break;";
@@ -968,33 +1007,31 @@
     code_ += "var container = encoder.container(keyedBy: CodingKeys.self)";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
-      auto name = Name(field);
-      auto type = field.value.type;
+      const auto type = field.value.type;
 
-      auto is_non_union_vector =
+      const auto is_non_union_vector =
           (field.value.type.base_type == BASE_TYPE_ARRAY ||
            field.value.type.base_type == BASE_TYPE_VECTOR) &&
           field.value.type.VectorType().base_type != BASE_TYPE_UTYPE;
 
-      code_.SetValue("RAWVALUENAME", field.name);
-      code_.SetValue("VALUENAME", name);
+      code_.SetValue("FIELDVAR", namer_.Variable(field));
       code_.SetValue("CONSTANT", field.value.constant);
       bool should_indent = true;
       if (is_non_union_vector) {
-        code_ += "if {{VALUENAME}}Count > 0 {";
+        code_ += "if {{FIELDVAR}}Count > 0 {";
       } else if (IsEnum(type) && !field.IsOptional()) {
         code_.SetValue("CONSTANT", GenEnumDefaultValue(field));
-        code_ += "if {{VALUENAME}} != {{CONSTANT}} {";
+        code_ += "if {{FIELDVAR}} != {{CONSTANT}} {";
       } else if (IsScalar(type.base_type) && !IsEnum(type) &&
                  !IsBool(type.base_type) && !field.IsOptional()) {
-        code_ += "if {{VALUENAME}} != {{CONSTANT}} {";
+        code_ += "if {{FIELDVAR}} != {{CONSTANT}} {";
       } else if (IsBool(type.base_type) && !field.IsOptional()) {
         std::string default_value =
             "0" == field.value.constant ? "false" : "true";
         code_.SetValue("CONSTANT", default_value);
-        code_ += "if {{VALUENAME}} != {{CONSTANT}} {";
+        code_ += "if {{FIELDVAR}} != {{CONSTANT}} {";
       } else {
         should_indent = false;
       }
@@ -1007,17 +1044,17 @@
                   IsEnum(type.VectorType()))) {
         code_ +=
             "var contentEncoder = container.nestedUnkeyedContainer(forKey: "
-            ".{{VALUENAME}})";
-        code_ += "for index in 0..<{{VALUENAME}}Count {";
+            ".{{FIELDVAR}})";
+        code_ += "for index in 0..<{{FIELDVAR}}Count {";
         Indent();
-        code_ += "guard let type = {{VALUENAME}}(at: index) else { continue }";
+        code_ += "guard let type = {{FIELDVAR}}(at: index) else { continue }";
         code_ += "try contentEncoder.encode(type)";
         Outdent();
         code_ += "}";
       } else {
         code_ +=
-            "try container.encodeIfPresent({{VALUENAME}}, forKey: "
-            ".{{VALUENAME}})";
+            "try container.encodeIfPresent({{FIELDVAR}}, forKey: "
+            ".{{FIELDVAR}})";
       }
       if (should_indent) Outdent();
 
@@ -1034,7 +1071,7 @@
     code_ += "";
     if (struct_def.fields.vec.empty() == false) GenerateCodingKeys(struct_def);
 
-    code_ += "public func encode(to encoder: Encoder) throws {";
+    code_ += "{{ACCESS_TYPE}} func encode(to encoder: Encoder) throws {";
     Indent();
     if (struct_def.fields.vec.empty() == false) GenerateEncoderBody(struct_def);
     Outdent();
@@ -1046,20 +1083,20 @@
 
   void GenerateVerifier(const StructDef &struct_def) {
     code_ +=
-        "public static func verify<T>(_ verifier: inout Verifier, at position: "
+        "{{ACCESS_TYPE}} static func verify<T>(_ verifier: inout Verifier, at "
+        "position: "
         "Int, of type: T.Type) throws where T: Verifiable {";
     Indent();
     code_ += "var _v = try verifier.visitTable(at: position)";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
-      auto offset = NumToString(field.value.offset);
-      auto name = Name(field);
+      const auto offset = NumToString(field.value.offset);
 
-      code_.SetValue("VALUENAME", name);
+      code_.SetValue("FIELDVAR", namer_.Variable(field));
       code_.SetValue("VALUETYPE", GenerateVerifierType(field));
-      code_.SetValue("OFFSET", name);
+      code_.SetValue("OFFSET", namer_.Field(field));
       code_.SetValue("ISREQUIRED", field.IsRequired() ? "true" : "false");
 
       if (IsUnion(field.value.type)) {
@@ -1069,7 +1106,7 @@
 
       code_ +=
           "try _v.visit(field: {{TABLEOFFSET}}.{{OFFSET}}.p, fieldName: "
-          "\"{{VALUENAME}}\", required: {{ISREQUIRED}}, type: "
+          "\"{{FIELDVAR}}\", required: {{ISREQUIRED}}, type: "
           "{{VALUETYPE}}.self)";
     }
     code_ += "_v.finish()";
@@ -1078,28 +1115,27 @@
   }
 
   void GenerateUnionTypeVerifier(const FieldDef &field) {
-    auto is_vector = IsVector(field.value.type) || IsArray(field.value.type);
+    const auto is_vector =
+        IsVector(field.value.type) || IsArray(field.value.type);
     if (field.value.type.base_type == BASE_TYPE_UTYPE ||
         (is_vector &&
          field.value.type.VectorType().base_type == BASE_TYPE_UTYPE))
       return;
     EnumDef &union_def = *field.value.type.enum_def;
-    code_.SetValue("VALUETYPE", NameWrappedInNameSpace(union_def));
+    code_.SetValue("VALUETYPE", namer_.NamespacedType(union_def));
     code_.SetValue("FUNCTION_NAME", is_vector ? "visitUnionVector" : "visit");
     code_ +=
         "try _v.{{FUNCTION_NAME}}(unionKey: {{TABLEOFFSET}}.{{OFFSET}}Type.p, "
         "unionField: {{TABLEOFFSET}}.{{OFFSET}}.p, unionKeyName: "
-        "\"{{VALUENAME}}Type\", fieldName: \"{{VALUENAME}}\", required: "
+        "\"{{FIELDVAR}}Type\", fieldName: \"{{FIELDVAR}}\", required: "
         "{{ISREQUIRED}}, completion: { (verifier, key: {{VALUETYPE}}, pos) in";
     Indent();
     code_ += "switch key {";
     for (auto it = union_def.Vals().begin(); it != union_def.Vals().end();
          ++it) {
       const auto &ev = **it;
-
-      auto name = Name(ev);
-      auto type = GenType(ev.union_type);
-      code_.SetValue("KEY", name);
+      const auto type = GenType(ev.union_type);
+      code_.SetValue("KEY", namer_.LegacySwiftVariant(ev));
       code_.SetValue("VALUETYPE", type);
       code_ += "case .{{KEY}}:";
       Indent();
@@ -1125,11 +1161,11 @@
   }
 
   std::string GenerateVerifierType(const FieldDef &field) {
-    auto type = field.value.type;
-    auto is_vector = IsVector(type) || IsArray(type);
+    const auto type = field.value.type;
+    const auto is_vector = IsVector(type) || IsArray(type);
 
     if (is_vector) {
-      auto vector_type = field.value.type.VectorType();
+      const auto vector_type = field.value.type.VectorType();
       return "ForwardOffset<Vector<" +
              GenerateNestedVerifierTypes(vector_type) + ", " +
              GenType(vector_type) + ">>";
@@ -1139,7 +1175,7 @@
   }
 
   std::string GenerateNestedVerifierTypes(const Type &type) {
-    auto string_type = GenType(type);
+    const auto string_type = GenType(type);
 
     if (IsScalar(type.base_type)) { return string_type; }
 
@@ -1153,7 +1189,7 @@
   void GenByKeyFunctions(const FieldDef &key_field) {
     code_.SetValue("TYPE", GenType(key_field.value.type));
     code_ +=
-        "{{ACCESS_TYPE}} func {{VALUENAME}}By(key: {{TYPE}}) -> {{VALUETYPE}}? "
+        "{{ACCESS_TYPE}} func {{FIELDVAR}}By(key: {{TYPE}}) -> {{VALUETYPE}}? "
         "{ \\";
     code_ += GenOffset() +
              "return o == 0 ? nil : {{VALUETYPE}}.lookupByKey(vector: "
@@ -1162,11 +1198,11 @@
 
   void GenEnum(const EnumDef &enum_def) {
     if (enum_def.generated) return;
-    auto is_private_access = enum_def.attributes.Lookup("private");
+    const auto is_private_access = enum_def.attributes.Lookup("private");
     code_.SetValue("ENUM_TYPE",
                    enum_def.is_union ? "UnionEnum" : "Enum, Verifiable");
     code_.SetValue("ACCESS_TYPE", is_private_access ? "internal" : "public");
-    code_.SetValue("ENUM_NAME", NameWrappedInNameSpace(enum_def));
+    code_.SetValue("ENUM_NAME", namer_.NamespacedType(enum_def));
     code_.SetValue("BASE_TYPE", GenTypeBasic(enum_def.underlying_type, false));
     GenComment(enum_def.doc_comment);
     code_ +=
@@ -1189,15 +1225,16 @@
         "{{ACCESS_TYPE}} var value: {{BASE_TYPE}} { return self.rawValue }";
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       const auto &ev = **it;
-      auto name = Name(ev);
-      code_.SetValue("KEY", name);
+      code_.SetValue("KEY", namer_.LegacySwiftVariant(ev));
       code_.SetValue("VALUE", enum_def.ToString(ev));
       GenComment(ev.doc_comment);
       code_ += "case {{KEY}} = {{VALUE}}";
     }
     code_ += "";
-    AddMinOrMaxEnumValue(Name(*enum_def.MaxValue()), "max");
-    AddMinOrMaxEnumValue(Name(*enum_def.MinValue()), "min");
+    AddMinOrMaxEnumValue(namer_.LegacySwiftVariant(*enum_def.MaxValue()),
+                         "max");
+    AddMinOrMaxEnumValue(namer_.LegacySwiftVariant(*enum_def.MinValue()),
+                         "min");
     Outdent();
     code_ += "}\n";
     if (parser_.opts.gen_json_coders) EnumEncoder(enum_def);
@@ -1235,8 +1272,7 @@
     code_ += "switch self {";
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       const auto &ev = **it;
-      auto name = Name(ev);
-      code_.SetValue("KEY", name);
+      code_.SetValue("KEY", namer_.LegacySwiftVariant(ev));
       code_.SetValue("RAWKEY", ev.name);
       code_ += "case .{{KEY}}: try container.encode(\"{{RAWKEY}}\")";
     }
@@ -1249,18 +1285,18 @@
 
   // MARK: - Object API
 
-  void GenerateObjectAPIExtensionHeader(std::string name) {
+  void GenerateObjectAPIExtensionHeader(std::string type_name) {
     code_ += "\n";
-    code_ += "{{ACCESS_TYPE}} mutating func unpack() -> " + name + " {";
+    code_ += "{{ACCESS_TYPE}} mutating func unpack() -> " + type_name + " {";
     Indent();
-    code_ += "return " + name + "(&self)";
+    code_ += "return " + type_name + "(&self)";
     Outdent();
     code_ += "}";
     code_ +=
         "{{ACCESS_TYPE}} static func pack(_ builder: inout FlatBufferBuilder, "
         "obj: "
         "inout " +
-        name + "?) -> Offset {";
+        type_name + "?) -> Offset {";
     Indent();
     code_ += "guard var obj = obj else { return Offset() }";
     code_ += "return pack(&builder, obj: &obj)";
@@ -1271,7 +1307,7 @@
         "{{ACCESS_TYPE}} static func pack(_ builder: inout FlatBufferBuilder, "
         "obj: "
         "inout " +
-        name + ") -> Offset {";
+        type_name + ") -> Offset {";
     Indent();
   }
 
@@ -1281,40 +1317,39 @@
     Indent();
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
 
-      auto name = Name(field);
-      auto type = GenType(field.value.type);
-      code_.SetValue("VALUENAME", name);
+      const auto type = GenType(field.value.type);
+      code_.SetValue("FIELDVAR", namer_.Variable(field));
       if (IsStruct(field.value.type)) {
-        code_ += "var _v{{VALUENAME}} = _t.{{VALUENAME}}";
-        code_ += "_{{VALUENAME}} = _v{{VALUENAME}}.unpack()";
+        code_ += "var _v{{FIELDVAR}} = _t.{{FIELDVAR}}";
+        code_ += "_{{FIELDVAR}} = _v{{FIELDVAR}}.unpack()";
         continue;
       }
       std::string is_enum = IsEnum(field.value.type) ? ".value" : "";
-      code_ += "_{{VALUENAME}} = _t.{{VALUENAME}}" + is_enum;
+      code_ += "_{{FIELDVAR}} = _t.{{FIELDVAR}}" + is_enum;
     }
     Outdent();
     code_ += "}\n";
   }
 
   void GenObjectAPI(const StructDef &struct_def) {
-    code_ += "{{ACCESS_TYPE}} class " + ObjectAPIName("{{STRUCTNAME}}") +
-             ": NativeObject {\n";
+    code_ += "{{ACCESS_TYPE}} class " +
+             namer_.NamespacedObjectType(struct_def) + ": NativeObject {\n";
     std::vector<std::string> buffer_constructor;
     std::vector<std::string> base_constructor;
     Indent();
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
       BuildObjectAPIConstructorBody(field, struct_def.fixed, buffer_constructor,
                                     base_constructor);
     }
     code_ += "";
     BuildObjectConstructor(buffer_constructor,
-                           "_ _t: inout " + NameWrappedInNameSpace(struct_def));
+                           "_ _t: inout " + namer_.NamespacedType(struct_def));
     BuildObjectConstructor(base_constructor);
     if (!struct_def.fixed)
       code_ +=
@@ -1326,35 +1361,37 @@
   }
 
   void GenerateObjectAPITableExtension(const StructDef &struct_def) {
-    GenerateObjectAPIExtensionHeader(ObjectAPIName("{{STRUCTNAME}}"));
+    GenerateObjectAPIExtensionHeader(namer_.NamespacedObjectType(struct_def));
     std::vector<std::string> unpack_body;
     std::string builder = ", &builder)";
     for (auto it = struct_def.fields.vec.begin();
          it != struct_def.fields.vec.end(); ++it) {
-      auto &field = **it;
+      const auto &field = **it;
       if (field.deprecated) continue;
-      auto name = Name(field);
-      auto type = GenType(field.value.type);
+      const auto field_var = namer_.Variable(field);
+      const auto field_field = namer_.Field(field);
+      const auto field_method = namer_.Method(field);
+      const auto type = GenType(field.value.type);
       std::string check_if_vector =
           (IsVector(field.value.type) || IsArray(field.value.type))
               ? "VectorOf("
               : "(";
-      std::string body = "add" + check_if_vector + name + ": ";
+      std::string body = "add" + check_if_vector + field_method + ": ";
       switch (field.value.type.base_type) {
         case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();
         case BASE_TYPE_VECTOR: {
-          GenerateVectorObjectAPITableExtension(field, name, type);
-          unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + name +
+          GenerateVectorObjectAPITableExtension(field);
+          unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + field_var +
                                 builder);
           break;
         }
         case BASE_TYPE_UNION: {
-          code_ += "let __" + name + " = obj." + name +
+          code_ += "let __" + field_var + " = obj." + field_var +
                    "?.pack(builder: &builder) ?? Offset()";
-          unpack_body.push_back("if let o = obj." + name + "?.type {");
-          unpack_body.push_back("  {{STRUCTNAME}}.add(" + name + "Type: o" +
-                                builder);
-          unpack_body.push_back("  {{STRUCTNAME}}." + body + "__" + name +
+          unpack_body.push_back("if let o = obj." + field_var + "?.type {");
+          unpack_body.push_back("  {{STRUCTNAME}}.add(" + field_var +
+                                "Type: o" + builder);
+          unpack_body.push_back("  {{STRUCTNAME}}." + body + "__" + field_var +
                                 builder);
           unpack_body.push_back("}\n");
           break;
@@ -1368,31 +1405,31 @@
             GenerateStructArgs(*field.value.type.struct_def, &code, "", "",
                                "$0", true);
             code = code.substr(0, code.size() - 2);
-            unpack_body.push_back("{{STRUCTNAME}}." + body + "obj." + name +
-                                  builder);
+            unpack_body.push_back("{{STRUCTNAME}}." + body + "obj." +
+                                  field_field + builder);
           } else {
-            code_ += "let __" + name + " = " + type +
-                     ".pack(&builder, obj: &obj." + name + ")";
-            unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + name +
+            code_ += "let __" + field_var + " = " + type +
+                     ".pack(&builder, obj: &obj." + field_field + ")";
+            unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + field_var +
                                   builder);
           }
           break;
         }
         case BASE_TYPE_STRING: {
-          unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + name +
+          unpack_body.push_back("{{STRUCTNAME}}." + body + "__" + field_var +
                                 builder);
           if (field.IsRequired()) {
-            code_ +=
-                "let __" + name + " = builder.create(string: obj." + name + ")";
+            code_ += "let __" + field_var + " = builder.create(string: obj." +
+                     field_field + ")";
           } else {
-            BuildingOptionalObjects(name, "builder.create(string: s)");
+            BuildingOptionalObjects(field_field, "builder.create(string: s)");
           }
           break;
         }
         case BASE_TYPE_UTYPE: break;
         default:
-          unpack_body.push_back("{{STRUCTNAME}}." + body + "obj." + name +
-                                builder);
+          unpack_body.push_back("{{STRUCTNAME}}." + body + "obj." +
+                                field_field + builder);
       }
     }
     code_ += "let __root = {{STRUCTNAME}}.start{{SHORT_STRUCTNAME}}(&builder)";
@@ -1405,79 +1442,79 @@
     code_ += "}";
   }
 
-  void GenerateVectorObjectAPITableExtension(const FieldDef &field,
-                                             const std::string &name,
-                                             const std::string &type) {
-    auto vectortype = field.value.type.VectorType();
+  void GenerateVectorObjectAPITableExtension(const FieldDef &field_def) {
+    const Type &field_type = field_def.value.type;
+    const auto type = GenType(field_type);
+    const auto var = namer_.Variable(field_def);
+    const auto field = namer_.Field(field_def);
+
+    const auto vectortype = field_type.VectorType();
     switch (vectortype.base_type) {
       case BASE_TYPE_UNION: {
-        code_ += "var __" + name + "__: [Offset] = []";
-        code_ += "for i in obj." + name + " {";
+        code_ += "var __" + var + "__: [Offset] = []";
+        code_ += "for i in obj." + var + " {";
         Indent();
         code_ += "guard let off = i?.pack(builder: &builder) else { continue }";
-        code_ += "__" + name + "__.append(off)";
+        code_ += "__" + var + "__.append(off)";
         Outdent();
         code_ += "}";
-        code_ += "let __" + name + " = builder.createVector(ofOffsets: __" +
-                 name + "__)";
-        code_ += "let __" + name + "Type = builder.createVector(obj." + name +
+        code_ += "let __" + var + " = builder.createVector(ofOffsets: __" +
+                 var + "__)";
+        code_ += "let __" + var + "Type = builder.createVector(obj." + field +
                  ".compactMap { $0?.type })";
         break;
       }
       case BASE_TYPE_UTYPE: break;
       case BASE_TYPE_STRUCT: {
-        if (field.value.type.struct_def &&
-            !field.value.type.struct_def->fixed) {
-          code_ += "var __" + name + "__: [Offset] = []";
-          code_ += "for var i in obj." + name + " {";
+        if (field_type.struct_def && !field_type.struct_def->fixed) {
+          code_ += "var __" + var + "__: [Offset] = []";
+          code_ += "for var i in obj." + var + " {";
           Indent();
           code_ +=
-              "__" + name + "__.append(" + type + ".pack(&builder, obj: &i))";
+              "__" + var + "__.append(" + type + ".pack(&builder, obj: &i))";
           Outdent();
           code_ += "}";
-          code_ += "let __" + name + " = builder.createVector(ofOffsets: __" +
-                   name + "__)";
+          code_ += "let __" + var + " = builder.createVector(ofOffsets: __" +
+                   var + "__)";
         } else {
-          code_ += "{{STRUCTNAME}}.startVectorOf" +
-                   ConvertCase(name, Case::kUpperCamel) + "(obj." + name +
-                   ".count, in: &builder)";
+          code_ += "{{STRUCTNAME}}." + namer_.Method("start_vector_of", var) +
+                   "(obj." + field + ".count, in: &builder)";
           std::string code;
-          GenerateStructArgs(*field.value.type.struct_def, &code, "", "", "_o",
-                             true);
+          GenerateStructArgs(*field_type.struct_def, &code, "", "", "_o", true);
           code = code.substr(0, code.size() - 2);
-          code_ += "for i in obj." + name + " {";
+          code_ += "for i in obj." + field + " {";
           Indent();
           code_ += "guard let _o = i else { continue }";
           code_ += "builder.create(struct: _o)";
           Outdent();
           code_ += "}";
-          code_ += "let __" + name + " = builder.endVector(len: obj." + name +
+          code_ += "let __" + var + " = builder.endVector(len: obj." + field +
                    ".count)";
         }
         break;
       }
       case BASE_TYPE_STRING: {
-        code_ += "let __" + name + " = builder.createVector(ofStrings: obj." +
-                 name + ".compactMap({ $0 }) )";
+        code_ += "let __" + var + " = builder.createVector(ofStrings: obj." +
+                 var + ".compactMap({ $0 }) )";
         break;
       }
       default: {
-        code_ += "let __" + name + " = builder.createVector(obj." + name + ")";
+        code_ += "let __" + var + " = builder.createVector(obj." + field + ")";
         break;
       }
     }
   }
 
-  void BuildingOptionalObjects(const std::string &name,
+  void BuildingOptionalObjects(const std::string &var,
                                const std::string &body_front) {
-    code_ += "let __" + name + ": Offset";
-    code_ += "if let s = obj." + name + " {";
+    code_ += "let __" + var + ": Offset";
+    code_ += "if let s = obj." + var + " {";
     Indent();
-    code_ += "__" + name + " = " + body_front;
+    code_ += "__" + var + " = " + body_front;
     Outdent();
     code_ += "} else {";
     Indent();
-    code_ += "__" + name + " = Offset()";
+    code_ += "__" + var + " = Offset()";
     Outdent();
     code_ += "}";
     code_ += "";
@@ -1497,120 +1534,125 @@
       const FieldDef &field, bool is_fixed,
       std::vector<std::string> &buffer_constructor,
       std::vector<std::string> &base_constructor) {
-    auto name = Name(field);
-    auto type = GenType(field.value.type);
-    code_.SetValue("VALUENAME", name);
+    const auto field_field = namer_.Field(field);
+    const auto field_var = namer_.Variable(field);
+    const auto type = GenType(field.value.type);
+    code_.SetValue("FIELDVAR", field_field);
     code_.SetValue("VALUETYPE", type);
     std::string is_required = field.IsRequired() ? "" : "?";
 
     switch (field.value.type.base_type) {
       case BASE_TYPE_STRUCT: {
-        type = GenType(field.value.type, true);
-        code_.SetValue("VALUETYPE", type);
-        auto optional =
+        const auto objtype = GenType(field.value.type, true);
+        code_.SetValue("VALUETYPE", objtype);
+        const auto optional =
             (field.value.type.struct_def && field.value.type.struct_def->fixed);
         std::string question_mark =
             (field.IsRequired() || (optional && is_fixed) ? "" : "?");
 
         code_ +=
-            "{{ACCESS_TYPE}} var {{VALUENAME}}: {{VALUETYPE}}" + question_mark;
-        base_constructor.push_back("" + name + " = " + type + "()");
+            "{{ACCESS_TYPE}} var {{FIELDVAR}}: {{VALUETYPE}}" + question_mark;
+        base_constructor.push_back("" + field_var + " = " + objtype + "()");
 
         if (field.value.type.struct_def->fixed) {
-          buffer_constructor.push_back("" + name + " = _t." + name);
+          buffer_constructor.push_back("" + field_var + " = _t." + field_field);
         } else {
-          buffer_constructor.push_back("var __" + name + " = _t." + name);
+          buffer_constructor.push_back("var __" + field_var + " = _t." +
+                                       field_field);
           buffer_constructor.push_back(
-              "" + name + " = __" + name +
+              "" + field_var + " = __" + field_var +
               (field.IsRequired() ? "!" : question_mark) + ".unpack()");
         }
         break;
       }
       case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();
       case BASE_TYPE_VECTOR: {
-        BuildObjectAPIConstructorBodyVectors(field, name, buffer_constructor,
+        BuildObjectAPIConstructorBodyVectors(field, buffer_constructor,
                                              base_constructor, "    ");
         break;
       }
       case BASE_TYPE_STRING: {
-        code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: String" + is_required;
-        buffer_constructor.push_back(name + " = _t." + name);
+        code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: String" + is_required;
+        buffer_constructor.push_back(field_var + " = _t." + field_field);
 
         if (field.IsRequired()) {
           std::string default_value =
               field.IsDefault() ? field.value.constant : "";
-          base_constructor.push_back(name + " = \"" + default_value + "\"");
+          base_constructor.push_back(field_var + " = \"" + default_value +
+                                     "\"");
           break;
         }
         if (field.IsDefault() && !field.IsRequired()) {
           std::string value = field.IsDefault() ? field.value.constant : "nil";
-          base_constructor.push_back(name + " = \"" + value + "\"");
+          base_constructor.push_back(field_var + " = \"" + value + "\"");
         }
         break;
       }
       case BASE_TYPE_UTYPE: break;
       case BASE_TYPE_UNION: {
-        BuildUnionEnumSwitchCase(*field.value.type.enum_def, name,
+        BuildUnionEnumSwitchCase(*field.value.type.enum_def, field_var,
                                  buffer_constructor);
         break;
       }
       default: {
-        buffer_constructor.push_back(name + " = _t." + name);
+        buffer_constructor.push_back(field_var + " = _t." + field_field);
         std::string nullable = field.IsOptional() ? "?" : "";
         if (IsScalar(field.value.type.base_type) &&
             !IsBool(field.value.type.base_type) && !IsEnum(field.value.type)) {
-          code_ +=
-              "{{ACCESS_TYPE}} var {{VALUENAME}}: {{VALUETYPE}}" + nullable;
+          code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: {{VALUETYPE}}" + nullable;
           if (!field.IsOptional())
-            base_constructor.push_back(name + " = " + field.value.constant);
+            base_constructor.push_back(field_var + " = " +
+                                       field.value.constant);
           break;
         }
 
         if (IsEnum(field.value.type)) {
-          auto default_value = IsEnum(field.value.type)
-                                   ? GenEnumDefaultValue(field)
-                                   : field.value.constant;
-          code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: {{VALUETYPE}}";
-          base_constructor.push_back(name + " = " + default_value);
+          const auto default_value = IsEnum(field.value.type)
+                                         ? GenEnumDefaultValue(field)
+                                         : field.value.constant;
+          code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: {{VALUETYPE}}";
+          base_constructor.push_back(field_var + " = " + default_value);
           break;
         }
 
         if (IsBool(field.value.type.base_type)) {
-          code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: Bool" + nullable;
+          code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: Bool" + nullable;
           std::string default_value =
               "0" == field.value.constant ? "false" : "true";
           if (!field.IsOptional())
-            base_constructor.push_back(name + " = " + default_value);
+            base_constructor.push_back(field_var + " = " + default_value);
         }
       }
     }
   }
 
   void BuildObjectAPIConstructorBodyVectors(
-      const FieldDef &field, const std::string &name,
-      std::vector<std::string> &buffer_constructor,
+      const FieldDef &field, std::vector<std::string> &buffer_constructor,
       std::vector<std::string> &base_constructor,
       const std::string &indentation) {
-    auto vectortype = field.value.type.VectorType();
+    const auto vectortype = field.value.type.VectorType();
+    const auto field_var = namer_.Field(field);
+    const auto field_field = namer_.Field(field);
 
     if (vectortype.base_type != BASE_TYPE_UTYPE) {
-      buffer_constructor.push_back(name + " = []");
-      buffer_constructor.push_back("for index in 0..<_t." + name + "Count {");
-      base_constructor.push_back(name + " = []");
+      buffer_constructor.push_back(field_var + " = []");
+      buffer_constructor.push_back("for index in 0..<_t." + field_field +
+                                   "Count {");
+      base_constructor.push_back(field_var + " = []");
     }
 
     switch (vectortype.base_type) {
       case BASE_TYPE_STRUCT: {
         code_.SetValue("VALUETYPE", GenType(vectortype, true));
-        code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: [{{VALUETYPE}}?]";
+        code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: [{{VALUETYPE}}?]";
         if (!vectortype.struct_def->fixed) {
-          buffer_constructor.push_back(indentation + "var __v_ = _t." + name +
-                                       "(at: index)");
-          buffer_constructor.push_back(indentation + name +
+          buffer_constructor.push_back(indentation + "var __v_ = _t." +
+                                       field_field + "(at: index)");
+          buffer_constructor.push_back(indentation + field_var +
                                        ".append(__v_?.unpack())");
         } else {
-          buffer_constructor.push_back(indentation + name + ".append(_t." +
-                                       name + "(at: index))");
+          buffer_constructor.push_back(indentation + field_var + ".append(_t." +
+                                       field_var + "(at: index))");
         }
         break;
       }
@@ -1619,7 +1661,7 @@
         break;
       }
       case BASE_TYPE_UNION: {
-        BuildUnionEnumSwitchCase(*field.value.type.enum_def, name,
+        BuildUnionEnumSwitchCase(*field.value.type.enum_def, field_var,
                                  buffer_constructor, indentation, true);
         break;
       }
@@ -1628,18 +1670,18 @@
         code_.SetValue(
             "VALUETYPE",
             (IsString(vectortype) ? "String?" : GenType(vectortype)));
-        code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: [{{VALUETYPE}}]";
+        code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: [{{VALUETYPE}}]";
 
         if (IsEnum(vectortype) && vectortype.base_type != BASE_TYPE_UNION) {
-          auto default_value = IsEnum(field.value.type)
-                                   ? GenEnumDefaultValue(field)
-                                   : field.value.constant;
-          buffer_constructor.push_back(indentation + name + ".append(_t." +
-                                       name + "(at: index)!)");
+          const auto default_value = IsEnum(field.value.type)
+                                         ? GenEnumDefaultValue(field)
+                                         : field.value.constant;
+          buffer_constructor.push_back(indentation + field_var + ".append(_t." +
+                                       field_field + "(at: index)!)");
           break;
         }
-        buffer_constructor.push_back(indentation + name + ".append(_t." + name +
-                                     "(at: index))");
+        buffer_constructor.push_back(indentation + field_var + ".append(_t." +
+                                     field_field + "(at: index))");
         break;
       }
     }
@@ -1647,19 +1689,17 @@
       buffer_constructor.push_back("}");
   }
 
-  void BuildUnionEnumSwitchCaseWritter(const EnumDef &ev) {
-    auto field_name = Name(ev);
-    code_.SetValue("VALUETYPE", field_name);
+  void BuildUnionEnumSwitchCaseWritter(const EnumDef &ed) {
     code_ += "switch type {";
-    for (auto it = ev.Vals().begin(); it < ev.Vals().end(); ++it) {
-      auto field = **it;
-      auto ev_name = Name(field);
-      auto type = GenType(field.union_type);
-      auto is_struct = IsStruct(field.union_type) ? type + Mutable() : type;
-      if (field.union_type.base_type == BASE_TYPE_NONE) { continue; }
-      code_ += "case ." + ev_name + ":";
+    for (auto it = ed.Vals().begin(); it < ed.Vals().end(); ++it) {
+      const auto ev = **it;
+      const auto variant = namer_.LegacySwiftVariant(ev);
+      const auto type = GenType(ev.union_type);
+      const auto is_struct = IsStruct(ev.union_type) ? type + Mutable() : type;
+      if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
+      code_ += "case ." + variant + ":";
       Indent();
-      code_ += "var __obj = value as? " + GenType(field.union_type, true);
+      code_ += "var __obj = value as? " + GenType(ev.union_type, true);
       code_ += "return " + is_struct + ".pack(&builder, obj: &__obj)";
       Outdent();
     }
@@ -1667,34 +1707,34 @@
     code_ += "}";
   }
 
-  void BuildUnionEnumSwitchCase(const EnumDef &ev, const std::string &name,
+  void BuildUnionEnumSwitchCase(const EnumDef &ed, const std::string &field,
                                 std::vector<std::string> &buffer_constructor,
                                 const std::string &indentation = "",
                                 const bool is_vector = false) {
-    auto field_name = NameWrappedInNameSpace(ev);
-    code_.SetValue("VALUETYPE", field_name);
-    code_ += "{{ACCESS_TYPE}} var {{VALUENAME}}: \\";
+    const auto ns_type = namer_.NamespacedType(ed);
+    code_.SetValue("VALUETYPE", ns_type);
+    code_ += "{{ACCESS_TYPE}} var {{FIELDVAR}}: \\";
     code_ += is_vector ? "[{{VALUETYPE}}Union?]" : "{{VALUETYPE}}Union?";
 
-    auto vector_reader = is_vector ? "(at: index" : "";
-    buffer_constructor.push_back(indentation + "switch _t." + name + "Type" +
+    const auto vector_reader = is_vector ? "(at: index" : "";
+    buffer_constructor.push_back(indentation + "switch _t." + field + "Type" +
                                  vector_reader + (is_vector ? ")" : "") + " {");
 
-    for (auto it = ev.Vals().begin(); it < ev.Vals().end(); ++it) {
-      auto field = **it;
-      auto ev_name = Name(field);
-      if (field.union_type.base_type == BASE_TYPE_NONE) { continue; }
-      auto type = IsStruct(field.union_type)
-                      ? GenType(field.union_type) + Mutable()
-                      : GenType(field.union_type);
-      buffer_constructor.push_back(indentation + "case ." + ev_name + ":");
+    for (auto it = ed.Vals().begin(); it < ed.Vals().end(); ++it) {
+      const auto ev = **it;
+      const auto variant = namer_.LegacySwiftVariant(ev);
+      if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
+      const auto type = IsStruct(ev.union_type)
+                            ? GenType(ev.union_type) + Mutable()
+                            : GenType(ev.union_type);
+      buffer_constructor.push_back(indentation + "case ." + variant + ":");
       buffer_constructor.push_back(
-          indentation + "  var _v = _t." + name + (is_vector ? "" : "(") +
+          indentation + "  var _v = _t." + field + (is_vector ? "" : "(") +
           vector_reader + (is_vector ? ", " : "") + "type: " + type + ".self)");
-      auto constructor =
-          field_name + "Union(_v?.unpack(), type: ." + ev_name + ")";
+      const auto constructor =
+          ns_type + "Union(_v?.unpack(), type: ." + variant + ")";
       buffer_constructor.push_back(
-          indentation + "  " + name +
+          indentation + "  " + field +
           (is_vector ? ".append(" + constructor + ")" : " = " + constructor));
     }
     buffer_constructor.push_back(indentation + "default: break");
@@ -1702,13 +1742,14 @@
   }
 
   void AddMinOrMaxEnumValue(const std::string &str, const std::string &type) {
-    auto current_value = str;
+    const auto current_value = str;
     code_.SetValue(type, current_value);
     code_ += "{{ACCESS_TYPE}} static var " + type +
              ": {{ENUM_NAME}} { return .{{" + type + "}} }";
   }
 
-  void GenLookup(const FieldDef &key_field) {
+  void GenLookup(const FieldDef &key_field, const std::string &struct_type) {
+    code_.SetValue("STRUCTTYPE", struct_type);
     code_.SetValue("OFFSET", NumToString(key_field.value.offset));
     std::string offset_reader =
         "Table.offset(Int32(fbb.capacity) - tableOffset, vOffset: {{OFFSET}}, "
@@ -1718,7 +1759,7 @@
     code_ +=
         "fileprivate static func lookupByKey(vector: Int32, key: {{TYPE}}, "
         "fbb: "
-        "ByteBuffer) -> {{VALUENAME}}? {";
+        "ByteBuffer) -> {{STRUCTTYPE}}? {";
     Indent();
     if (IsString(key_field.value.type))
       code_ += "let key = key.utf8.map { $0 }";
@@ -1748,7 +1789,7 @@
     Outdent();
     code_ += "} else {";
     Indent();
-    code_ += "return {{VALUENAME}}(fbb, o: tableOffset)";
+    code_ += "return {{STRUCTTYPE}}(fbb, o: tableOffset)";
     Outdent();
     code_ += "}";
     Outdent();
@@ -1762,7 +1803,7 @@
     if (field.padding) {
       for (int i = 0; i < 4; i++) {
         if (static_cast<int>(field.padding) & (1 << i)) {
-          auto bits = (1 << i) * 8;
+          const auto bits = (1 << i) * 8;
           code_ += "private let padding" + NumToString((*id)++) + "__: UInt" +
                    NumToString(bits) + " = 0";
         }
@@ -1784,8 +1825,7 @@
   }
 
   std::string GenReaderMainBody(const std::string &optional = "") {
-    return "{{ACCESS_TYPE}} var {{VALUENAME}}: {{VALUETYPE}}" + optional +
-           " { ";
+    return "{{ACCESS_TYPE}} var {{FIELDVAR}}: {{VALUETYPE}}" + optional + " { ";
   }
 
   std::string GenReader(const std::string &type,
@@ -1799,36 +1839,33 @@
 
   std::string GenMutate(const std::string &offset,
                         const std::string &get_offset, bool isRaw = false) {
-    return "@discardableResult {{ACCESS_TYPE}} func mutate({{VALUENAME}}: "
+    return "@discardableResult {{ACCESS_TYPE}} func mutate({{FIELDVAR}}: "
            "{{VALUETYPE}}) -> Bool {" +
-           get_offset + " return {{ACCESS}}.mutate({{VALUENAME}}" +
+           get_offset + " return {{ACCESS}}.mutate({{FIELDVAR}}" +
            (isRaw ? ".rawValue" : "") + ", index: " + offset + ") }";
   }
 
   std::string GenMutateArray() {
-    return "{{ACCESS_TYPE}} func mutate({{VALUENAME}}: {{VALUETYPE}}, at "
-           "index: "
-           "Int32) -> Bool { " +
+    return "{{ACCESS_TYPE}} func mutate({{FIELDVAR}}: {{VALUETYPE}}, at "
+           "index: Int32) -> Bool { " +
            GenOffset() +
-           "return {{ACCESS}}.directMutate({{VALUENAME}}, index: "
+           "return {{ACCESS}}.directMutate({{FIELDVAR}}, index: "
            "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }";
   }
 
   std::string GenEnumDefaultValue(const FieldDef &field) {
-    auto &value = field.value;
+    const auto &value = field.value;
     FLATBUFFERS_ASSERT(value.type.enum_def);
-    auto &enum_def = *value.type.enum_def;
+    const auto &enum_def = *value.type.enum_def;
     // Vector of enum defaults are always "[]" which never works.
     const std::string constant = IsVector(value.type) ? "0" : value.constant;
-    auto enum_val = enum_def.FindByValue(constant);
-    std::string name;
+    const auto enum_val = enum_def.FindByValue(constant);
     if (enum_val) {
-      name = Name(*enum_val);
+      return "." + namer_.LegacySwiftVariant(*enum_val);
     } else {
       const auto &ev = **enum_def.Vals().begin();
-      name = Name(ev);
+      return "." + namer_.LegacySwiftVariant(ev);
     }
-    return "." + name;
   }
 
   std::string GenEnumConstructor(const std::string &at) {
@@ -1836,15 +1873,14 @@
   }
 
   std::string ValidateFunc() {
-    return "static func validateVersion() { FlatBuffersVersion_2_0_0() }";
+    return "static func validateVersion() { FlatBuffersVersion_2_0_8() }";
   }
 
   std::string GenType(const Type &type,
                       const bool should_consider_suffix = false) const {
-    return IsScalar(type.base_type)
-               ? GenTypeBasic(type)
-               : (IsArray(type) ? GenType(type.VectorType())
-                                : GenTypePointer(type, should_consider_suffix));
+    return IsScalar(type.base_type) ? GenTypeBasic(type)
+           : IsArray(type)          ? GenType(type.VectorType())
+                           : GenTypePointer(type, should_consider_suffix);
   }
 
   std::string GenTypePointer(const Type &type,
@@ -1853,12 +1889,11 @@
       case BASE_TYPE_STRING: return "String";
       case BASE_TYPE_VECTOR: return GenType(type.VectorType());
       case BASE_TYPE_STRUCT: {
-        auto &struct_ = *type.struct_def;
-        if (should_consider_suffix && !struct_.fixed) {
-          return WrapInNameSpace(struct_.defined_namespace,
-                                 ObjectAPIName(Name(struct_)));
+        const auto &sd = *type.struct_def;
+        if (should_consider_suffix && !sd.fixed) {
+          return namer_.NamespacedObjectType(sd);
         }
-        return WrapInNameSpace(struct_.defined_namespace, Name(struct_));
+        return namer_.NamespacedType(sd);
       }
       case BASE_TYPE_UNION:
       default: return "FlatbuffersInitializable";
@@ -1869,22 +1904,10 @@
     return GenTypeBasic(type, true);
   }
 
-  std::string ObjectAPIName(const std::string &name) const {
-    return parser_.opts.object_prefix + name + parser_.opts.object_suffix;
-  }
-
   void Indent() { code_.IncrementIdentLevel(); }
 
   void Outdent() { code_.DecrementIdentLevel(); }
 
-  std::string NameWrappedInNameSpace(const EnumDef &enum_def) const {
-    return WrapInNameSpace(enum_def.defined_namespace, Name(enum_def));
-  }
-
-  std::string NameWrappedInNameSpace(const StructDef &struct_def) const {
-    return WrapInNameSpace(struct_def.defined_namespace, Name(struct_def));
-  }
-
   std::string GenTypeBasic(const Type &type, bool can_override) const {
     // clang-format off
     static const char * const swift_type[] = {
@@ -1896,29 +1919,15 @@
     };
     // clang-format on
     if (can_override) {
-      if (type.enum_def) return NameWrappedInNameSpace(*type.enum_def);
+      if (type.enum_def) return namer_.NamespacedType(*type.enum_def);
       if (type.base_type == BASE_TYPE_BOOL) return "Bool";
     }
     return swift_type[static_cast<int>(type.base_type)];
   }
 
-  std::string EscapeKeyword(const std::string &name) const {
-    return keywords_.find(name) == keywords_.end() ? name : name + "_";
-  }
-
   std::string Mutable() const { return "_Mutable"; }
 
-  std::string Name(const EnumVal &ev) const {
-    auto name = ev.name;
-    if (isupper(name.front())) {
-      std::transform(name.begin(), name.end(), name.begin(), CharToLower);
-    }
-    return EscapeKeyword(ConvertCase(name, Case::kLowerCamel));
-  }
-
-  std::string Name(const Definition &def) const {
-    return EscapeKeyword(ConvertCase(def.name, Case::kLowerCamel));
-  }
+  IdlNamer namer_;
 };
 }  // namespace swift
 bool GenerateSwift(const Parser &parser, const std::string &path,
diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp
index 805e934..3b69c95 100644
--- a/src/idl_gen_text.cpp
+++ b/src/idl_gen_text.cpp
@@ -248,11 +248,23 @@
   template<typename T>
   bool GenField(const FieldDef &fd, const Table *table, bool fixed,
                 int indent) {
-    return PrintScalar(
-        fixed ? reinterpret_cast<const Struct *>(table)->GetField<T>(
-                    fd.value.offset)
-              : table->GetField<T>(fd.value.offset, GetFieldDefault<T>(fd)),
-        fd.value.type, indent);
+    if (fixed) {
+      return PrintScalar(
+          reinterpret_cast<const Struct *>(table)->GetField<T>(fd.value.offset),
+          fd.value.type, indent);
+    } else if (fd.IsOptional()) {
+      auto opt = table->GetOptional<T, T>(fd.value.offset);
+      if (opt) {
+        return PrintScalar(*opt, fd.value.type, indent);
+      } else {
+        text += "null";
+        return true;
+      }
+    } else {
+      return PrintScalar(
+          table->GetField<T>(fd.value.offset, GetFieldDefault<T>(fd)),
+          fd.value.type, indent);
+    }
   }
 
   // Generate text for non-scalar field.
diff --git a/src/idl_gen_ts.cpp b/src/idl_gen_ts.cpp
index faf4345..32ab863 100644
--- a/src/idl_gen_ts.cpp
+++ b/src/idl_gen_ts.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-// independent from idl_parser, since this code is not needed for most clients
 #include <algorithm>
 #include <cassert>
 #include <unordered_map>
@@ -24,21 +23,67 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
+#include "idl_namer.h"
 
 namespace flatbuffers {
-
+namespace {
 struct ImportDefinition {
   std::string name;
   std::string import_statement;
   std::string export_statement;
   std::string bare_file_path;
   std::string rel_file_path;
+  std::string object_name;
   const Definition *dependent = nullptr;
   const Definition *dependency = nullptr;
 };
 
+Namer::Config TypeScriptDefaultConfig() {
+  return { /*types=*/Case::kKeep,
+           /*constants=*/Case::kUnknown,
+           /*methods=*/Case::kLowerCamel,
+           /*functions=*/Case::kLowerCamel,
+           /*fields=*/Case::kLowerCamel,
+           /*variables=*/Case::kLowerCamel,
+           /*variants=*/Case::kKeep,
+           /*enum_variant_seperator=*/"::",
+           /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
+           /*namespaces=*/Case::kKeep,
+           /*namespace_seperator=*/"_",
+           /*object_prefix=*/"",
+           /*object_suffix=*/"T",
+           /*keyword_prefix=*/"",
+           /*keyword_suffix=*/"_",
+           /*filenames=*/Case::kDasher,
+           /*directories=*/Case::kDasher,
+           /*output_path=*/"",
+           /*filename_suffix=*/"_generated",
+           /*filename_extension=*/".ts" };
+}
+
+std::set<std::string> TypescriptKeywords() {
+  // List of keywords retrieved from here:
+  // https://github.com/microsoft/TypeScript/issues/2536
+  return {
+    "arguments", "break",    "case",      "catch",      "class",      "const",
+    "continue",  "debugger", "default",   "delete",     "do",         "else",
+    "enum",      "export",   "extends",   "false",      "finally",    "for",
+    "function",  "if",       "import",    "in",         "instanceof", "new",
+    "null",      "Object",   "return",    "super",      "switch",     "this",
+    "throw",     "true",     "try",       "typeof",     "var",        "void",
+    "while",     "with",     "as",        "implements", "interface",  "let",
+    "package",   "private",  "protected", "public",     "static",     "yield",
+  };
+}
+
 enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
 
+template<typename T> struct SupportsObjectAPI : std::false_type {};
+
+template<> struct SupportsObjectAPI<StructDef> : std::true_type {};
+
+}  // namespace
+
 namespace ts {
 // Iterate through all definitions we haven't generate code for (enums, structs,
 // and tables) and output them to a single file.
@@ -48,83 +93,53 @@
 
   TsGenerator(const Parser &parser, const std::string &path,
               const std::string &file_name)
-      : BaseGenerator(parser, path, file_name, "", ".", "ts") {
-    // clang-format off
+      : BaseGenerator(parser, path, file_name, "", "_", "ts"),
+        namer_(WithFlagOptions(TypeScriptDefaultConfig(), parser.opts, path),
+               TypescriptKeywords()) {}
 
-    // List of keywords retrieved from here:
-    // https://github.com/microsoft/TypeScript/issues/2536
-    // One per line to ease comparisons to that list are easier
-    static const char *const keywords[] = {
-      "argument",
-      "break",
-      "case",
-      "catch",
-      "class",
-      "const",
-      "continue",
-      "debugger",
-      "default",
-      "delete",
-      "do",
-      "else",
-      "enum",
-      "export",
-      "extends",
-      "false",
-      "finally",
-      "for",
-      "function",
-      "if",
-      "import",
-      "in",
-      "instanceof",
-      "new",
-      "null",
-      "Object",
-      "return",
-      "super",
-      "switch",
-      "this",
-      "throw",
-      "true",
-      "try",
-      "typeof",
-      "var",
-      "void",
-      "while",
-      "with",
-      "as",
-      "implements",
-      "interface",
-      "let",
-      "package",
-      "private",
-      "protected",
-      "public",
-      "static",
-      "yield",
-      nullptr,
-      // clang-format on
-    };
-
-    for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
-  }
   bool generate() {
-    if (parser_.opts.ts_flat_file && parser_.opts.generate_all) {
-      // Not implemented; warning message should have beem emitted by flatc.
-      return false;
-    }
     generateEnums();
     generateStructs();
     generateEntry();
     return true;
   }
 
+  bool IncludeNamespace() const {
+    // When generating a single flat file and all its includes, namespaces are
+    // important to avoid type name clashes.
+    return parser_.opts.ts_flat_file && parser_.opts.generate_all;
+  }
+
+  std::string GetTypeName(const EnumDef &def, const bool = false,
+                          const bool force_ns_wrap = false) {
+    if (IncludeNamespace() || force_ns_wrap) {
+      return namer_.NamespacedType(def);
+    }
+    return namer_.Type(def);
+  }
+
+  std::string GetTypeName(const StructDef &def, const bool object_api = false,
+                          const bool force_ns_wrap = false) {
+    if (object_api && parser_.opts.generate_object_based_api) {
+      if (IncludeNamespace() || force_ns_wrap) {
+        return namer_.NamespacedObjectType(def);
+      } else {
+        return namer_.ObjectType(def);
+      }
+    } else {
+      if (IncludeNamespace() || force_ns_wrap) {
+        return namer_.NamespacedType(def);
+      } else {
+        return namer_.Type(def);
+      }
+    }
+  }
+
   // Save out the generated code for a single class while adding
   // declaration boilerplate.
-  bool SaveType(const Definition &definition, const std::string &classcode,
+  bool SaveType(const Definition &definition, const std::string &class_code,
                 import_set &imports, import_set &bare_imports) {
-    if (!classcode.length()) return true;
+    if (!class_code.length()) return true;
 
     std::string code;
 
@@ -144,25 +159,24 @@
       if (!imports.empty()) code += "\n\n";
     }
 
-    code += classcode;
-    auto filename =
-        NamespaceDir(*definition.defined_namespace, true) +
-        ConvertCase(definition.name, Case::kDasher, Case::kUpperCamel) + ".ts";
+    code += class_code;
+
     if (parser_.opts.ts_flat_file) {
       flat_file_ += code;
+      flat_file_ += "\n";
       flat_file_definitions_.insert(&definition);
       return true;
     } else {
-      return SaveFile(filename.c_str(), code, false);
+      auto dirs = namer_.Directories(*definition.defined_namespace);
+      EnsureDirExists(dirs);
+      auto basename = dirs + namer_.File(definition, SkipFile::Suffix);
+
+      return SaveFile(basename.c_str(), code, false);
     }
   }
 
  private:
-  std::unordered_set<std::string> keywords_;
-
-  std::string EscapeKeyword(const std::string &name) const {
-    return keywords_.find(name) == keywords_.end() ? name : name + "_";
-  }
+  IdlNamer namer_;
 
   import_set imports_all_;
 
@@ -177,6 +191,10 @@
   // This maps from import names to types to import.
   std::map<std::string, std::map<std::string, std::string>>
       flat_file_import_declarations_;
+  // For flat file codegen, tracks whether we need to import the flatbuffers
+  // library itself (not necessary for files that solely consist of enum
+  // definitions).
+  bool import_flatbuffers_lib_ = false;
 
   // Generate code for all enums.
   void generateEnums() {
@@ -200,6 +218,7 @@
       import_set bare_imports;
       import_set imports;
       AddImport(bare_imports, "* as flatbuffers", "flatbuffers");
+      import_flatbuffers_lib_ = true;
       auto &struct_def = **it;
       std::string declcode;
       GenStruct(parser_, struct_def, &declcode, imports);
@@ -210,38 +229,45 @@
 
   // Generate code for a single entry point module.
   void generateEntry() {
-    std::string code;
+    std::string code =
+        "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
     if (parser_.opts.ts_flat_file) {
-      code += "import * as flatbuffers from 'flatbuffers';\n";
-      for (const auto &it : flat_file_import_declarations_) {
-        // Note that we do end up generating an import for ourselves, which
-        // should generally be harmless.
-        // TODO: Make it so we don't generate a self-import; this will also
-        // require modifying AddImport to ensure that we don't use
-        // namespace-prefixed names anywhere...
-        std::string file = it.first;
-        if (file.empty()) {
-          continue;
-        }
-        std::string noext = flatbuffers::StripExtension(file);
-        std::string basename = flatbuffers::StripPath(noext);
-        std::string include_file = GeneratedFileName(
-            parser_.opts.include_prefix,
-            parser_.opts.keep_include_path ? noext : basename, parser_.opts);
-        // TODO: what is the right behavior when different include flags are
-        // specified here? Should we always be adding the "./" for a relative
-        // path or turn it off if --include-prefix is specified, or something
-        // else?
-        std::string include_name = "./" + flatbuffers::StripExtension(include_file);
-        code += "import {";
-        for (const auto &pair : it.second) {
-          code += EscapeKeyword(pair.first) + " as " +
-                  EscapeKeyword(pair.second) + ", ";
-        }
-        code.resize(code.size() - 2);
-        code += "} from  '" + include_name + "';\n";
+      if (import_flatbuffers_lib_) {
+        code += "import * as flatbuffers from 'flatbuffers';\n";
+        code += "\n";
       }
-      code += "\n\n";
+      // Only include import statements when not generating all.
+      if (!parser_.opts.generate_all) {
+        for (const auto &it : flat_file_import_declarations_) {
+          // Note that we do end up generating an import for ourselves, which
+          // should generally be harmless.
+          // TODO: Make it so we don't generate a self-import; this will also
+          // require modifying AddImport to ensure that we don't use
+          // namespace-prefixed names anywhere...
+          std::string file = it.first;
+          if (file.empty()) { continue; }
+          std::string noext = flatbuffers::StripExtension(file);
+          std::string basename = flatbuffers::StripPath(noext);
+          std::string include_file = GeneratedFileName(
+              parser_.opts.include_prefix,
+              parser_.opts.keep_prefix ? noext : basename, parser_.opts);
+          // TODO: what is the right behavior when different include flags are
+          // specified here? Should we always be adding the "./" for a relative
+          // path or turn it off if --include-prefix is specified, or something
+          // else?
+          std::string include_name =
+              "./" + flatbuffers::StripExtension(include_file);
+          code += "import {";
+          for (const auto &pair : it.second) {
+            code += namer_.EscapeKeyword(pair.first) + " as " +
+                    namer_.EscapeKeyword(pair.second) + ", ";
+          }
+          code.resize(code.size() - 2);
+          code += "} from '" + include_name + ".js';\n";
+        }
+        code += "\n";
+      }
+
       code += flat_file_;
       const std::string filename =
           GeneratedFileName(path_, file_name_, parser_.opts);
@@ -250,7 +276,8 @@
       for (auto it = imports_all_.begin(); it != imports_all_.end(); it++) {
         code += it->second.export_statement + "\n";
       }
-      std::string path = "./" + path_ + file_name_ + ".ts";
+      const std::string path =
+          GeneratedFileName(path_, file_name_, parser_.opts);
       SaveFile(path.c_str(), code, false);
     }
   }
@@ -286,7 +313,9 @@
     if (reverse) return;  // FIXME.
     std::string &code = *code_ptr;
     GenDocComment(enum_def.doc_comment, code_ptr);
-    code += "export enum " + EscapeKeyword(enum_def.name) + "{\n";
+    code += "export enum ";
+    code += GetTypeName(enum_def);
+    code += " {\n";
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       auto &ev = **it;
       if (!ev.doc_comment.empty()) {
@@ -294,15 +323,13 @@
         GenDocComment(ev.doc_comment, code_ptr, "  ");
       }
 
-      const std::string escaped_name = EscapeKeyword(ev.name);
-
       // Generate mapping between EnumName: EnumValue(int)
       if (reverse) {
         code += "  '" + enum_def.ToString(ev) + "'";
         code += " = ";
-        code += "'" + escaped_name + "'";
+        code += "'" + namer_.Variant(ev) + "'";
       } else {
-        code += "  " + escaped_name;
+        code += "  " + namer_.Variant(ev);
         code += " = ";
         // Unfortunately, because typescript does not support bigint enums,
         // for 64-bit enums, we instead map the enum names to strings.
@@ -324,7 +351,7 @@
       code += GenUnionConvFunc(enum_def.underlying_type, imports);
     }
 
-    code += "\n\n";
+    code += "\n";
   }
 
   static std::string GenType(const Type &type) {
@@ -359,8 +386,8 @@
         return GenBBAccess() + ".__union_with_string" + arguments;
       case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
       default: {
-        auto getter = GenBBAccess() + ".read" +
-                      ConvertCase(GenType(type), Case::kUpperCamel) + arguments;
+        auto getter = GenBBAccess() + "." +
+                      namer_.Method("read_" + GenType(type)) + arguments;
         if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
         return getter;
       }
@@ -385,9 +412,10 @@
         }
         default: {
           if (auto val = value.type.enum_def->FindByValue(value.constant)) {
-            return EscapeKeyword(AddImport(imports, *value.type.enum_def,
-                                           *value.type.enum_def)) +
-                   "." + EscapeKeyword(val->name);
+            return AddImport(imports, *value.type.enum_def,
+                             *value.type.enum_def)
+                       .name +
+                   "." + namer_.Variant(*val);
           } else {
             return value.constant;
           }
@@ -411,7 +439,9 @@
         return "BigInt('" + value.constant + "')";
       }
 
-      default: return value.constant;
+      default:
+        if (value.constant == "nan") { return "NaN"; }
+        return value.constant;
     }
   }
 
@@ -424,7 +454,7 @@
         if (IsString(type)) {
           name = "string|Uint8Array";
         } else {
-          name = EscapeKeyword(AddImport(imports, owner, *type.struct_def));
+          name = AddImport(imports, owner, *type.struct_def).name;
         }
         return allowNull ? (name + "|null") : name;
       }
@@ -437,7 +467,8 @@
       default:
         if (IsScalar(type.base_type)) {
           if (type.enum_def) {
-            const auto enum_name = AddImport(imports, owner, *type.enum_def);
+            const auto enum_name =
+                AddImport(imports, owner, *type.enum_def).name;
             return allowNull ? (enum_name + "|null") : enum_name;
           }
           return allowNull ? "number|null" : "number";
@@ -447,7 +478,7 @@
   }
 
   // Returns the method name for use with add/put calls.
-  static std::string GenWriteMethod(const Type &type) {
+  std::string GenWriteMethod(const Type &type) {
     // Forward to signed versions since unsigned versions don't exist
     switch (type.base_type) {
       case BASE_TYPE_UTYPE:
@@ -458,9 +489,8 @@
       default: break;
     }
 
-    return IsScalar(type.base_type)
-               ? ConvertCase(GenType(type), Case::kUpperCamel)
-               : (IsStruct(type) ? "Struct" : "Offset");
+    return IsScalar(type.base_type) ? namer_.Type(GenType(type))
+                                    : (IsStruct(type) ? "Struct" : "Offset");
   }
 
   template<typename T> static std::string MaybeAdd(T value) {
@@ -490,8 +520,8 @@
     }
   }
 
-  static void GenStructBody(const StructDef &struct_def, std::string *body,
-                            const std::string &nameprefix) {
+  void GenStructBody(const StructDef &struct_def, std::string *body,
+                     const std::string &nameprefix) {
     *body += "  builder.prep(";
     *body += NumToString(struct_def.minalign) + ", ";
     *body += NumToString(struct_def.bytesize) + ");\n";
@@ -517,7 +547,7 @@
   }
 
   std::string GenerateNewExpression(const std::string &object_name) {
-    return "new " + EscapeKeyword(object_name) + "()";
+    return "new " + namer_.Type(object_name) + "()";
   }
 
   void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
@@ -563,16 +593,6 @@
     }
   }
 
-  static std::string GetObjApiClassName(const StructDef &sd,
-                                        const IDLOptions &opts) {
-    return GetObjApiClassName(sd.name, opts);
-  }
-
-  static std::string GetObjApiClassName(const std::string &name,
-                                        const IDLOptions &opts) {
-    return opts.object_prefix + name + opts.object_suffix;
-  }
-
   bool UnionHasStringType(const EnumDef &union_enum) {
     return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
                        [](const EnumVal *ev) {
@@ -601,7 +621,7 @@
       if (IsString(ev.union_type)) {
         type = "string";  // no need to wrap string type in namespace
       } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
-        type = AddImport(imports, union_enum, *ev.union_type.struct_def);
+        type = AddImport(imports, union_enum, *ev.union_type.struct_def).name;
       } else {
         FLATBUFFERS_ASSERT(false);
       }
@@ -615,156 +635,140 @@
     return ret;
   }
 
-  std::string AddImport(import_set &imports, const Definition &dependent,
-                        const StructDef &dependency) {
-    std::string ns;
-    const auto &depc_comps = dependency.defined_namespace->components;
-    for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
-      ns += *it;
-    }
-    std::string unique_name = ns + dependency.name;
-    std::string import_name = dependency.name;
-    std::string long_import_name;
-    if (imports.find(unique_name) != imports.end())
-      return imports.find(unique_name)->second.name;
+  static bool CheckIfNameClashes(const import_set &imports,
+                                 const std::string &name) {
+    // TODO: this would be better as a hashset.
     for (auto it = imports.begin(); it != imports.end(); it++) {
-      if (it->second.name == import_name) {
-        long_import_name = ns + import_name;
-        break;
-      }
+      if (it->second.name == name) { return true; }
     }
-
-    if (parser_.opts.ts_flat_file) {
-      std::string file = dependency.declaration_file == nullptr
-                                   ? dependency.file
-                                   : dependency.declaration_file->substr(2);
-      file = RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
-                                dependency.file).substr(2);
-      long_import_name = ns + import_name;
-      flat_file_import_declarations_[file][import_name] = long_import_name;
-      if (parser_.opts.generate_object_based_api) {
-        flat_file_import_declarations_[file][import_name + "T"] = long_import_name + "T";
-      }
-    }
-
-    std::string import_statement;
-    std::string export_statement;
-    import_statement += "import { ";
-    export_statement += "export { ";
-    std::string symbols_expression;
-    if (long_import_name.empty()) {
-      symbols_expression += EscapeKeyword(import_name);
-      if (parser_.opts.generate_object_based_api)
-        symbols_expression += ", " + import_name + "T";
-    } else {
-      symbols_expression += EscapeKeyword(dependency.name) + " as " +
-                            EscapeKeyword(long_import_name);
-      if (parser_.opts.generate_object_based_api)
-        symbols_expression +=
-            ", " + dependency.name + "T as " + long_import_name + "T";
-    }
-    import_statement += symbols_expression + " } from '";
-    export_statement += symbols_expression + " } from '";
-    std::string bare_file_path;
-    std::string rel_file_path;
-    const auto &dep_comps = dependent.defined_namespace->components;
-    for (size_t i = 0; i < dep_comps.size(); i++)
-      rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
-    if (dep_comps.size() == 0) rel_file_path += ".";
-    for (auto it = depc_comps.begin(); it != depc_comps.end(); it++)
-      bare_file_path +=
-          kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
-    bare_file_path +=
-        kPathSeparator +
-        ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
-    rel_file_path += bare_file_path;
-    import_statement += rel_file_path + "';";
-    export_statement += "." + bare_file_path + "';";
-    ImportDefinition import;
-    import.name = long_import_name.empty() ? import_name : long_import_name;
-    import.bare_file_path = bare_file_path;
-    import.rel_file_path = rel_file_path;
-    import.import_statement = import_statement;
-    import.export_statement = export_statement;
-    import.dependency = &dependency;
-    import.dependent = &dependent;
-    imports.insert(std::make_pair(unique_name, import));
-    return import.name;
+    return false;
   }
 
-  // TODO: largely (but not identical) duplicated code from above couln't find a
-  // good way to refactor
-  std::string AddImport(import_set &imports, const Definition &dependent,
-                        const EnumDef &dependency) {
-    std::string ns;
-    const auto &depc_comps = dependency.defined_namespace->components;
-    for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
-      ns += *it;
-    }
-    std::string unique_name = ns + dependency.name;
-    std::string import_name = EscapeKeyword(dependency.name);
-    std::string long_import_name;
-    if (imports.find(unique_name) != imports.end()) {
-      return imports.find(unique_name)->second.name;
-    }
-    for (auto it = imports.begin(); it != imports.end(); it++) {
-      if (it->second.name == import_name) {
-        long_import_name = ns + import_name;
-        break;
+  std::string GenSymbolExpression(const StructDef &struct_def,
+                                  const bool has_name_clash,
+                                  const std::string &import_name,
+                                  const std::string &name,
+                                  const std::string &object_name) {
+    std::string symbols_expression;
+
+    if (has_name_clash) {
+      // We have a name clash
+      symbols_expression += import_name + " as " + name;
+
+      if (parser_.opts.generate_object_based_api) {
+        symbols_expression += ", " +
+                              GetTypeName(struct_def, /*object_api =*/true) +
+                              " as " + object_name;
+      }
+    } else {
+      // No name clash, use the provided name
+      symbols_expression += name;
+
+      if (parser_.opts.generate_object_based_api) {
+        symbols_expression += ", " + object_name;
       }
     }
 
-    if (parser_.opts.ts_flat_file) {
-      std::string file = dependency.declaration_file == nullptr
-                                   ? dependency.file
-                                   : dependency.declaration_file->substr(2);
-      file = RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
-                                dependency.file).substr(2);
-      long_import_name = ns + import_name;
-      flat_file_import_declarations_[file][import_name] = long_import_name;
+    return symbols_expression;
+  }
+
+  std::string GenSymbolExpression(const EnumDef &enum_def,
+                                  const bool has_name_clash,
+                                  const std::string &import_name,
+                                  const std::string &name,
+                                  const std::string &) {
+    std::string symbols_expression;
+    if (has_name_clash) {
+      symbols_expression += import_name + " as " + name;
+    } else {
+      symbols_expression += name;
     }
 
-    std::string import_statement;
-    std::string export_statement;
-    import_statement += "import { ";
-    export_statement += "export { ";
-    std::string symbols_expression;
-    if (long_import_name.empty())
-      symbols_expression += import_name;
-    else
-      symbols_expression += EscapeKeyword(dependency.name) + " as " +
-                            EscapeKeyword(long_import_name);
-    if (dependency.is_union) {
-      symbols_expression += ", unionTo" + import_name;
-      symbols_expression += ", unionListTo" + import_name;
+    if (enum_def.is_union) {
+      symbols_expression += ", unionTo" + name;
+      symbols_expression += ", unionListTo" + name;
     }
-    import_statement += symbols_expression + " } from '";
-    export_statement += symbols_expression + " } from '";
+
+    return symbols_expression;
+  }
+
+  template<typename DefinitionT>
+  ImportDefinition AddImport(import_set &imports, const Definition &dependent,
+                             const DefinitionT &dependency) {
+    // The unique name of the dependency, fully qualified in its namespace.
+    const std::string unique_name = GetTypeName(
+        dependency, /*object_api = */ false, /*force_ns_wrap=*/true);
+
+    // Look if we have already added this import and return its name if found.
+    const auto import_pair = imports.find(unique_name);
+    if (import_pair != imports.end()) { return import_pair->second; }
+
+    // Check if this name would have a name clash with another type. Just use
+    // the "base" name (properly escaped) without any namespacing applied.
+    const std::string import_name = GetTypeName(dependency);
+    const bool has_name_clash = CheckIfNameClashes(imports, import_name);
+
+    // If we have a name clash, use the unique name, otherwise use simple name.
+    std::string name = has_name_clash ? unique_name : import_name;
+
+    const std::string object_name =
+        GetTypeName(dependency, /*object_api=*/true, has_name_clash);
+
+    if (parser_.opts.ts_flat_file) {
+      // In flat-file generation, do not attempt to import things from ourselves
+      // *and* do not wrap namespaces (note that this does override the logic
+      // above, but since we force all non-self-imports to use namespace-based
+      // names in flat file generation, it's fine).
+      if (dependent.file == dependency.file) {
+        name = import_name;
+      } else {
+        const std::string file =
+            RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
+                               dependency.file)
+                // Strip the leading //
+                .substr(2);
+        flat_file_import_declarations_[file][import_name] = name;
+
+        if (parser_.opts.generate_object_based_api &&
+            SupportsObjectAPI<DefinitionT>::value) {
+          flat_file_import_declarations_[file][import_name + "T"] = object_name;
+        }
+      }
+    }
+
+    const std::string symbols_expression = GenSymbolExpression(
+        dependency, has_name_clash, import_name, name, object_name);
+
     std::string bare_file_path;
     std::string rel_file_path;
     const auto &dep_comps = dependent.defined_namespace->components;
-    for (size_t i = 0; i < dep_comps.size(); i++)
+    for (size_t i = 0; i < dep_comps.size(); i++) {
       rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
-    if (dep_comps.size() == 0) rel_file_path += ".";
-    for (auto it = depc_comps.begin(); it != depc_comps.end(); it++)
-      bare_file_path +=
-          kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
+    }
+    if (dep_comps.size() == 0) { rel_file_path += "."; }
+
     bare_file_path +=
         kPathSeparator +
-        ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
+        namer_.Directories(dependency.defined_namespace->components,
+                           SkipDir::OutputPath) +
+        namer_.File(dependency, SkipFile::SuffixAndExtension);
     rel_file_path += bare_file_path;
-    import_statement += rel_file_path + "';";
-    export_statement += "." + bare_file_path + "';";
+
     ImportDefinition import;
-    import.name = long_import_name.empty() ? import_name : long_import_name;
+    import.name = name;
+    import.object_name = object_name;
     import.bare_file_path = bare_file_path;
     import.rel_file_path = rel_file_path;
-    import.import_statement = import_statement;
-    import.export_statement = export_statement;
+    import.import_statement =
+        "import { " + symbols_expression + " } from '" + rel_file_path + ".js';";
+    import.export_statement =
+        "export { " + symbols_expression + " } from '." + bare_file_path + ".js';";
     import.dependency = &dependency;
     import.dependent = &dependent;
+
     imports.insert(std::make_pair(unique_name, import));
-    return import.name;
+
+    return import;
   }
 
   void AddImport(import_set &imports, std::string import_name,
@@ -777,7 +781,9 @@
   }
 
   // Generate a TS union type based on a union's enum
-  std::string GenObjApiUnionTypeTS(import_set &imports, const IDLOptions &opts,
+  std::string GenObjApiUnionTypeTS(import_set &imports,
+                                   const StructDef &dependent,
+                                   const IDLOptions &,
                                    const EnumDef &union_enum) {
     std::string ret = "";
     std::set<std::string> type_list;
@@ -791,8 +797,8 @@
       if (IsString(ev.union_type)) {
         type = "string";  // no need to wrap string type in namespace
       } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
-        type = GetObjApiClassName(
-            AddImport(imports, union_enum, *ev.union_type.struct_def), opts);
+        type = AddImport(imports, dependent, *ev.union_type.struct_def)
+                   .object_name;
       } else {
         FLATBUFFERS_ASSERT(false);
       }
@@ -809,11 +815,11 @@
   }
 
   std::string GenUnionConvFuncName(const EnumDef &enum_def) {
-    return "unionTo" + enum_def.name;
+    return namer_.Function("unionTo", enum_def);
   }
 
   std::string GenUnionListConvFuncName(const EnumDef &enum_def) {
-    return "unionListTo" + enum_def.name;
+    return namer_.Function("unionListTo", enum_def);
   }
 
   std::string GenUnionConvFunc(const Type &union_type, import_set &imports) {
@@ -824,12 +830,12 @@
       const auto valid_union_type_with_null = valid_union_type + "|null";
 
       auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
-                 "(\n  type: " + enum_def.name +
+                 "(\n  type: " + GetTypeName(enum_def) +
                  ",\n  accessor: (obj:" + valid_union_type + ") => " +
                  valid_union_type_with_null +
                  "\n): " + valid_union_type_with_null + " {\n";
 
-      const auto enum_type = AddImport(imports, enum_def, enum_def);
+      const auto enum_type = AddImport(imports, enum_def, enum_def).name;
 
       const auto union_enum_loop = [&](const std::string &accessor_str) {
         ret += "  switch(" + enum_type + "[type]) {\n";
@@ -840,13 +846,13 @@
           const auto &ev = **it;
           if (ev.IsZero()) { continue; }
 
-          ret += "    case '" + ev.name + "': ";
+          ret += "    case '" + namer_.Variant(ev) + "': ";
 
           if (IsString(ev.union_type)) {
             ret += "return " + accessor_str + "'') as string;";
           } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
             const auto type =
-                AddImport(imports, enum_def, *ev.union_type.struct_def);
+                AddImport(imports, enum_def, *ev.union_type.struct_def).name;
             ret += "return " + accessor_str + "new " + type + "())! as " +
                    type + ";";
           } else {
@@ -863,7 +869,7 @@
       ret += "}";
 
       ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
-             "(\n  type: " + enum_def.name +
+             "(\n  type: " + GetTypeName(enum_def) +
              ", \n  accessor: (index: number, obj:" + valid_union_type +
              ") => " + valid_union_type_with_null +
              ", \n  index: number\n): " + valid_union_type_with_null + " {\n";
@@ -879,12 +885,13 @@
   // Used for generating a short function that returns the correct class
   // based on union enum type. Assume the context is inside the non object api
   // type
-  std::string GenUnionValTS(import_set &imports, const std::string &field_name,
+  std::string GenUnionValTS(import_set &imports, const StructDef &dependent,
+                            const std::string &field_name,
                             const Type &union_type,
                             const bool is_array = false) {
     if (union_type.enum_def) {
       const auto &enum_def = *union_type.enum_def;
-      const auto enum_type = AddImport(imports, enum_def, enum_def);
+      const auto enum_type = AddImport(imports, dependent, enum_def).name;
       const std::string union_accessor = "this." + field_name;
 
       const auto union_has_string = UnionHasStringType(enum_def);
@@ -894,11 +901,11 @@
 
       if (!is_array) {
         const auto conversion_function = GenUnionConvFuncName(enum_def);
-        const auto target_enum = "this." + field_name + "Type()";
 
         ret = "(() => {\n";
-        ret += "      let temp = " + conversion_function + "(" + target_enum +
-               ", " + field_binded_method + ");\n";
+        ret += "      let temp = " + conversion_function + "(this." +
+               namer_.Method(field_name, "Type") + "(), " +
+               field_binded_method + ");\n";
         ret += "      if(temp === null) { return null; }\n";
         ret += union_has_string
                    ? "      if(typeof temp === 'string') { return temp; }\n"
@@ -907,17 +914,15 @@
         ret += "  })()";
       } else {
         const auto conversion_function = GenUnionListConvFuncName(enum_def);
-        const auto target_enum_accesor = "this." + field_name + "Type";
-        const auto target_enum_length = target_enum_accesor + "Length()";
 
         ret = "(() => {\n";
         ret += "    let ret = [];\n";
-        ret += "    for(let targetEnumIndex = 0; targetEnumIndex < " +
-               target_enum_length +
+        ret += "    for(let targetEnumIndex = 0; targetEnumIndex < this." +
+               namer_.Method(field_name, "TypeLength") + "()" +
                "; "
                "++targetEnumIndex) {\n";
-        ret += "      let targetEnum = " + target_enum_accesor +
-               "(targetEnumIndex);\n";
+        ret += "      let targetEnum = this." +
+               namer_.Method(field_name, "Type") + "(targetEnumIndex);\n";
         ret += "      if(targetEnum === null || " + enum_type +
                "[targetEnum!] === 'NONE') { "
                "continue; }\n\n";
@@ -956,15 +961,20 @@
          it != struct_def.fields.vec.end(); ++it) {
       auto &field = **it;
 
-      const auto curr_member_accessor =
-          prefix + "." + ConvertCase(field.name, Case::kLowerCamel);
+      auto curr_member_accessor = prefix + "." + namer_.Method(field);
+      if (prefix != "this") {
+        curr_member_accessor = prefix + "?." + namer_.Method(field);
+      }
       if (IsStruct(field.value.type)) {
         ret += GenStructMemberValueTS(*field.value.type.struct_def,
                                       curr_member_accessor, delimiter);
       } else {
         if (nullCheck) {
-          ret +=
-              "(" + prefix + " === null ? 0 : " + curr_member_accessor + "!)";
+          std::string nullValue = "0";
+          if (field.value.type.base_type == BASE_TYPE_BOOL) {
+            nullValue = "false";
+          }
+          ret += "(" + curr_member_accessor + " ?? " + nullValue + ")";
         } else {
           ret += curr_member_accessor;
         }
@@ -979,7 +989,7 @@
   void GenObjApi(const Parser &parser, StructDef &struct_def,
                  std::string &obj_api_unpack_func, std::string &obj_api_class,
                  import_set &imports) {
-    const auto class_name = GetObjApiClassName(struct_def, parser.opts);
+    const auto class_name = GetTypeName(struct_def, /*object_api=*/true);
 
     std::string unpack_func = "\nunpack(): " + class_name +
                               " {\n  return new " + class_name + "(" +
@@ -999,8 +1009,7 @@
     std::string pack_func_offset_decl;
     std::string pack_func_create_call;
 
-    const auto struct_name =
-        EscapeKeyword(AddImport(imports, struct_def, struct_def));
+    const auto struct_name = AddImport(imports, struct_def, struct_def).name;
 
     if (has_create) {
       pack_func_create_call = "  return " + struct_name + ".create" +
@@ -1023,10 +1032,10 @@
       auto &field = **it;
       if (field.deprecated) continue;
 
-      const auto field_name = ConvertCase(field.name, Case::kLowerCamel);
-      const auto field_name_escaped = EscapeKeyword(field_name);
+      const auto field_method = namer_.Method(field);
+      const auto field_field = namer_.Field(field);
       const std::string field_binded_method =
-          "this." + field_name + ".bind(this)";
+          "this." + field_method + ".bind(this)";
 
       std::string field_val;
       std::string field_type;
@@ -1046,14 +1055,14 @@
 
         field_type += GenTypeName(imports, field, field.value.type, false,
                                   has_null_default);
-        field_val = "this." + field_name + "()";
+        field_val = "this." + namer_.Method(field) + "()";
 
         if (field.value.type.base_type != BASE_TYPE_STRING) {
-          field_offset_val = "this." + field_name_escaped;
+          field_offset_val = "this." + namer_.Field(field);
         } else {
           field_offset_decl = GenNullCheckConditional(
-              "this." + field_name_escaped,
-              "builder.createString(this." + field_name_escaped + "!)", "0");
+              "this." + namer_.Field(field),
+              "builder.createString(this." + field_field + "!)", "0");
         }
       }
 
@@ -1063,17 +1072,15 @@
         switch (field.value.type.base_type) {
           case BASE_TYPE_STRUCT: {
             const auto &sd = *field.value.type.struct_def;
-            field_type += GetObjApiClassName(AddImport(imports, struct_def, sd),
-                                             parser.opts);
+            field_type += AddImport(imports, struct_def, sd).object_name;
 
             const std::string field_accessor =
-                "this." + field_name_escaped + "()";
+                "this." + namer_.Method(field) + "()";
             field_val = GenNullCheckConditional(field_accessor,
                                                 field_accessor + "!.unpack()");
             auto packing = GenNullCheckConditional(
-                "this." + field_name_escaped,
-                "this." + field_name_escaped + "!.pack(builder)",
-                "0");
+                "this." + field_field,
+                "this." + field_field + "!.pack(builder)", "0");
 
             if (sd.fixed) {
               field_offset_val = std::move(packing);
@@ -1095,28 +1102,25 @@
             switch (vectortype.base_type) {
               case BASE_TYPE_STRUCT: {
                 const auto &sd = *field.value.type.struct_def;
-                field_type += GetObjApiClassName(sd, parser.opts);
+                field_type += GetTypeName(sd, /*object_api=*/true);
+                ;
                 field_type += ")[]";
 
                 field_val = GenBBAccess() + ".createObjList(" +
-                            field_binded_method + ", this." + field_name +
-                            "Length())";
+                            field_binded_method + ", this." +
+                            namer_.Method(field, "Length") + "())";
 
                 if (sd.fixed) {
                   field_offset_decl =
-                      "builder.createStructOffsetList(this." +
-                      field_name_escaped + ", " +
-                      EscapeKeyword(
-                          AddImport(imports, struct_def, struct_def)) +
-                      ".start" + ConvertCase(field_name, Case::kUpperCamel) +
-                      "Vector)";
+                      "builder.createStructOffsetList(this." + field_field +
+                      ", " + AddImport(imports, struct_def, struct_def).name +
+                      "." + namer_.Method("start", field, "Vector") + ")";
                 } else {
                   field_offset_decl =
-                      EscapeKeyword(
-                          AddImport(imports, struct_def, struct_def)) +
-                      ".create" + ConvertCase(field_name, Case::kUpperCamel) +
-                      "Vector(builder, builder.createObjectOffsetList(" +
-                      "this." + field_name_escaped + "))";
+                      AddImport(imports, struct_def, struct_def).name + "." +
+                      namer_.Method("create", field, "Vector") +
+                      "(builder, builder.createObjectOffsetList(" + "this." +
+                      field_field + "))";
                 }
 
                 break;
@@ -1125,28 +1129,28 @@
               case BASE_TYPE_STRING: {
                 field_type += "string)[]";
                 field_val = GenBBAccess() + ".createScalarList(" +
-                            field_binded_method + ", this." + field_name +
-                            "Length())";
+                            field_binded_method + ", this." +
+                            namer_.Field(field, "Length") + "())";
                 field_offset_decl =
-                    EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
-                    ".create" + ConvertCase(field_name, Case::kUpperCamel) +
-                    "Vector(builder, builder.createObjectOffsetList(" +
-                    "this." + field_name_escaped + "))";
+                    AddImport(imports, struct_def, struct_def).name + "." +
+                    namer_.Method("create", field, "Vector") +
+                    "(builder, builder.createObjectOffsetList(" + "this." +
+                    namer_.Field(field) + "))";
                 break;
               }
 
               case BASE_TYPE_UNION: {
-                field_type += GenObjApiUnionTypeTS(imports, parser.opts,
-                                                   *(vectortype.enum_def));
+                field_type += GenObjApiUnionTypeTS(
+                    imports, struct_def, parser.opts, *(vectortype.enum_def));
                 field_type += ")[]";
-                field_val =
-                    GenUnionValTS(imports, field_name, vectortype, true);
+                field_val = GenUnionValTS(imports, struct_def, field_method,
+                                          vectortype, true);
 
                 field_offset_decl =
-                    EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
-                    ".create" + ConvertCase(field_name, Case::kUpperCamel) +
-                    "Vector(builder, builder.createObjectOffsetList(" +
-                    "this." + field_name_escaped + "))";
+                    AddImport(imports, struct_def, struct_def).name + "." +
+                    namer_.Method("create", field, "Vector") +
+                    "(builder, builder.createObjectOffsetList(" + "this." +
+                    namer_.Field(field) + "))";
 
                 break;
               }
@@ -1159,13 +1163,13 @@
                 }
                 field_type += ")[]";
                 field_val = GenBBAccess() + ".createScalarList(" +
-                            field_binded_method + ", this." + field_name +
-                            "Length())";
+                            field_binded_method + ", this." +
+                            namer_.Method(field, "Length") + "())";
 
                 field_offset_decl =
-                    EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
-                    ".create" + ConvertCase(field_name, Case::kUpperCamel) +
-                    "Vector(builder, this." + field_name_escaped + ")";
+                    AddImport(imports, struct_def, struct_def).name + "." +
+                    namer_.Method("create", field, "Vector") +
+                    "(builder, this." + field_field + ")";
 
                 break;
               }
@@ -1175,12 +1179,13 @@
           }
 
           case BASE_TYPE_UNION: {
-            field_type += GenObjApiUnionTypeTS(imports, parser.opts,
+            field_type += GenObjApiUnionTypeTS(imports, struct_def, parser.opts,
                                                *(field.value.type.enum_def));
 
-            field_val = GenUnionValTS(imports, field_name, field.value.type);
+            field_val = GenUnionValTS(imports, struct_def, field_method,
+                                      field.value.type);
             field_offset_decl =
-                "builder.createObjectOffset(this." + field_name_escaped + ")";
+                "builder.createObjectOffset(this." + field_field + ")";
             break;
           }
 
@@ -1193,18 +1198,17 @@
 
       if (!field_offset_decl.empty()) {
         field_offset_decl =
-            "  const " + field_name_escaped + " = " + field_offset_decl + ";";
+            "  const " + field_field + " = " + field_offset_decl + ";";
       }
-      if (field_offset_val.empty()) { field_offset_val = field_name_escaped; }
+      if (field_offset_val.empty()) { field_offset_val = field_field; }
 
       unpack_func += "    " + field_val;
-      unpack_to_func += "  _o." + field_name_escaped + " = " + field_val + ";";
+      unpack_to_func += "  _o." + field_field + " = " + field_val + ";";
 
-      // FIXME: if field_type and field_name_escaped are identical, then
+      // FIXME: if field_type and field_field are identical, then
       // this generates invalid typescript.
-      constructor_func += "  public " + field_name_escaped + ": " + field_type +
-                          " = " +
-                          field_default_val;
+      constructor_func += "  public " + field_field + ": " + field_type +
+                          " = " + field_default_val;
 
       if (!struct_def.fixed) {
         if (!field_offset_decl.empty()) {
@@ -1215,11 +1219,12 @@
           pack_func_create_call += field_offset_val;
         } else {
           if (field.IsScalarOptional()) {
-            pack_func_create_call += "  if (" + field_offset_val + " !== null)\n  ";
+            pack_func_create_call +=
+                "  if (" + field_offset_val + " !== null)\n  ";
           }
-          pack_func_create_call += "  " + struct_name + ".add" +
-                                   ConvertCase(field.name, Case::kUpperCamel) +
-                                   "(builder, " + field_offset_val + ");\n";
+          pack_func_create_call += "  " + struct_name + "." +
+                                   namer_.Method("add", field) + "(builder, " +
+                                   field_offset_val + ");\n";
         }
       }
 
@@ -1252,10 +1257,10 @@
       pack_func_create_call += "return " + struct_name + ".end" +
                                GetPrefixedName(struct_def) + "(builder);";
     }
-
-    obj_api_class = "\nexport class " +
-                    GetObjApiClassName(struct_def, parser.opts) + " {\n";
-
+    obj_api_class = "\n";
+    obj_api_class += "export class ";
+    obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
+    obj_api_class += " {\n";
     obj_api_class += constructor_func;
     obj_api_class += pack_func_prototype + pack_func_offset_decl +
                      pack_func_create_call + "\n}";
@@ -1286,13 +1291,18 @@
     if (struct_def.generated) return;
     std::string &code = *code_ptr;
 
-    std::string object_name;
-    std::string object_namespace = GetNameSpace(struct_def);
+    // Special case for the root struct, since no one will necessarily reference
+    // it, we have to explicitly add it to the import list.
+    if (&struct_def == parser_.root_struct_def_) {
+      AddImport(imports, struct_def, struct_def);
+    }
+
+    const std::string object_name = GetTypeName(struct_def);
 
     // Emit constructor
-    object_name = EscapeKeyword(struct_def.name);
     GenDocComment(struct_def.doc_comment, code_ptr);
-    code += "export class " + object_name;
+    code += "export class ";
+    code += object_name;
     code += " {\n";
     code += "  bb: flatbuffers.ByteBuffer|null = null;\n";
     code += "  bb_pos = 0;\n";
@@ -1300,7 +1310,7 @@
     // Generate the __init method that sets the field in a pre-existing
     // accessor object. This is to allow object reuse.
     code +=
-        "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
+        "  __init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
     code += "  this.bb_pos = i;\n";
     code += "  this.bb = bb;\n";
     code += "  return this;\n";
@@ -1337,7 +1347,7 @@
         const auto has_null_default = is_string || HasNullDefault(field);
 
         GenDocComment(field.doc_comment, code_ptr);
-        std::string prefix = ConvertCase(field.name, Case::kLowerCamel) + "(";
+        std::string prefix = namer_.Method(field) + "(";
         if (is_string) {
           code += prefix + "):string|null\n";
           code +=
@@ -1380,10 +1390,11 @@
       else {
         switch (field.value.type.base_type) {
           case BASE_TYPE_STRUCT: {
-            const auto type = EscapeKeyword(
-                AddImport(imports, struct_def, *field.value.type.struct_def));
+            const auto type =
+                AddImport(imports, struct_def, *field.value.type.struct_def)
+                    .name;
             GenDocComment(field.doc_comment, code_ptr);
-            code += ConvertCase(field.name, Case::kLowerCamel);
+            code += namer_.Method(field);
             code += "(obj?:" + type + "):" + type + "|null {\n";
 
             if (struct_def.fixed) {
@@ -1423,7 +1434,7 @@
               default: ret_type = vectortypename;
             }
             GenDocComment(field.doc_comment, code_ptr);
-            std::string prefix = ConvertCase(field.name, Case::kLowerCamel);
+            std::string prefix = namer_.Method(field);
             // TODO: make it work without any
             // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
             if (is_union) { prefix += ""; }
@@ -1483,7 +1494,7 @@
 
           case BASE_TYPE_UNION: {
             GenDocComment(field.doc_comment, code_ptr);
-            code += ConvertCase(field.name, Case::kLowerCamel);
+            code += namer_.Method(field);
 
             const auto &union_enum = *(field.value.type.enum_def);
             const auto union_type = GenUnionGenericTypeTS(union_enum);
@@ -1508,12 +1519,15 @@
         std::string type =
             GenTypeName(imports, struct_def, field.value.type, true);
 
-        code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
+        code += namer_.LegacyTsMutateMethod(field) + "(value:" + type +
+                "):boolean {\n";
+
+        const std::string write_method =
+            "." + namer_.Method("write", GenType(field.value.type));
 
         if (struct_def.fixed) {
-          code += "  " + GenBBAccess() + ".write" +
-                  ConvertCase(GenType(field.value.type), Case::kUpperCamel) +
-                  "(this.bb_pos + " + NumToString(field.value.offset) + ", ";
+          code += "  " + GenBBAccess() + write_method + "(this.bb_pos + " +
+                  NumToString(field.value.offset) + ", ";
         } else {
           code += "  const offset = " + GenBBAccess() +
                   ".__offset(this.bb_pos, " + NumToString(field.value.offset) +
@@ -1523,9 +1537,8 @@
           code += "  }\n\n";
 
           // special case for bools, which are treated as uint8
-          code += "  " + GenBBAccess() + ".write" +
-                  ConvertCase(GenType(field.value.type), Case::kUpperCamel) +
-                  "(this.bb_pos + offset, ";
+          code +=
+              "  " + GenBBAccess() + write_method + "(this.bb_pos + offset, ";
           if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
         }
 
@@ -1538,8 +1551,8 @@
       if (IsVector(field.value.type)) {
         // Emit a length helper
         GenDocComment(code_ptr);
-        code += ConvertCase(field.name, Case::kLowerCamel);
-        code += "Length():number {\n" + offset_prefix;
+        code += namer_.Method(field, "Length");
+        code += "():number {\n" + offset_prefix;
 
         code +=
             GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
@@ -1549,9 +1562,9 @@
         if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
           GenDocComment(code_ptr);
 
-          code += ConvertCase(field.name, Case::kLowerCamel);
-          code += "Array():" + GenType(vectorType) + "Array|null {\n" +
-                  offset_prefix;
+          code += namer_.Method(field, "Array");
+          code +=
+              "():" + GenType(vectorType) + "Array|null {\n" + offset_prefix;
 
           code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
                   ".bytes().buffer, " + GenBBAccess() +
@@ -1610,7 +1623,7 @@
 
         // Generate the field insertion method
         GenDocComment(code_ptr);
-        code += "static add" + ConvertCase(field.name, Case::kUpperCamel);
+        code += "static " + namer_.Method("add", field);
         code += "(builder:flatbuffers.Builder, " + argname + ":" +
                 GetArgType(imports, struct_def, field, false) + ") {\n";
         code += "  builder.addField" + GenWriteMethod(field.value.type) + "(";
@@ -1641,8 +1654,8 @@
             GenDocComment(code_ptr);
 
             const std::string sig_begin =
-                "static create" + ConvertCase(field.name, Case::kUpperCamel) +
-                "Vector(builder:flatbuffers.Builder, data:";
+                "static " + namer_.Method("create", field, "Vector") +
+                "(builder:flatbuffers.Builder, data:";
             const std::string sig_end = "):flatbuffers.Offset";
             std::string type =
                 GenTypeName(imports, struct_def, vector_type, true) + "[]";
@@ -1679,8 +1692,9 @@
           // after
           GenDocComment(code_ptr);
 
-          code += "static start" + ConvertCase(field.name, Case::kUpperCamel);
-          code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
+          code += "static ";
+          code += namer_.Method("start", field, "Vector");
+          code += "(builder:flatbuffers.Builder, numElems:number) {\n";
           code += "  builder.startVector(" + NumToString(elem_size);
           code += ", numElems, " + NumToString(alignment) + ");\n";
           code += "}\n\n";
@@ -1723,8 +1737,8 @@
         }
 
         code += "):flatbuffers.Offset {\n";
-        code += "  " + object_name + ".start" +
-                GetPrefixedName(struct_def) + "(builder);\n";
+        code += "  " + object_name + ".start" + GetPrefixedName(struct_def) +
+                "(builder);\n";
 
         std::string methodPrefix = object_name;
         for (auto it = struct_def.fields.vec.begin();
@@ -1738,8 +1752,7 @@
             code += "  if (" + arg_name + " !== null)\n  ";
           }
 
-          code += "  " + methodPrefix + ".add" +
-                  ConvertCase(field.name, Case::kUpperCamel) + "(";
+          code += "  " + methodPrefix + "." + namer_.Method("add", field) + "(";
           code += "builder, " + arg_name + ");\n";
         }
 
@@ -1757,10 +1770,9 @@
       code += "}\n";
 
       code += "\n";
-      code += "static deserialize(buffer: Uint8Array):" + EscapeKeyword(name) +
-              " {\n";
-      code += "  return " +
-              EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
+      code += "static deserialize(buffer: Uint8Array):" +
+              namer_.EscapeKeyword(name) + " {\n";
+      code += "  return " + AddImport(imports, struct_def, struct_def).name +
               ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
       code += "}\n";
     }
@@ -1788,12 +1800,8 @@
   }
 
   std::string GetArgName(const FieldDef &field) {
-    auto argname = ConvertCase(field.name, Case::kLowerCamel);
-    if (!IsScalar(field.value.type.base_type)) {
-      argname += "Offset";
-    } else {
-      argname = EscapeKeyword(argname);
-    }
+    auto argname = namer_.Variable(field);
+    if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
     return argname;
   }
 
diff --git a/src/idl_namer.h b/src/idl_namer.h
new file mode 100644
index 0000000..f60e30c
--- /dev/null
+++ b/src/idl_namer.h
@@ -0,0 +1,168 @@
+#ifndef FLATBUFFERS_IDL_NAMER
+#define FLATBUFFERS_IDL_NAMER
+
+#include "flatbuffers/idl.h"
+#include "namer.h"
+
+namespace flatbuffers {
+
+// Provides Namer capabilities to types defined in the flatbuffers IDL.
+class IdlNamer : public Namer {
+ public:
+  explicit IdlNamer(Config config, std::set<std::string> keywords)
+      : Namer(config, std::move(keywords)) {}
+
+  using Namer::Constant;
+  using Namer::Directories;
+  using Namer::Field;
+  using Namer::File;
+  using Namer::Function;
+  using Namer::Method;
+  using Namer::Namespace;
+  using Namer::NamespacedType;
+  using Namer::ObjectType;
+  using Namer::Type;
+  using Namer::Variable;
+  using Namer::Variant;
+
+  std::string Constant(const FieldDef &d) const { return Constant(d.name); }
+
+  // Types are always structs or enums so we can only expose these two
+  // overloads.
+  std::string Type(const StructDef &d) const { return Type(d.name); }
+  std::string Type(const EnumDef &d) const { return Type(d.name); }
+
+  std::string Function(const Definition &s) const { return Function(s.name); }
+  std::string Function(const std::string& prefix, const Definition &s) const {
+    return Function(prefix + s.name);
+  }
+
+  std::string Field(const FieldDef &s) const { return Field(s.name); }
+  std::string Field(const FieldDef &d, const std::string &s) const {
+    return Field(d.name + "_" + s);
+  }
+
+  std::string Variable(const FieldDef &s) const { return Variable(s.name); }
+
+  std::string Variable(const StructDef &s) const { return Variable(s.name); }
+
+  std::string Variant(const EnumVal &s) const { return Variant(s.name); }
+
+  std::string EnumVariant(const EnumDef &e, const EnumVal &v) const {
+    return Type(e) + config_.enum_variant_seperator + Variant(v);
+  }
+
+  std::string ObjectType(const StructDef &d) const {
+    return ObjectType(d.name);
+  }
+  std::string ObjectType(const EnumDef &d) const { return ObjectType(d.name); }
+
+  std::string Method(const FieldDef &d, const std::string &suffix) const {
+    return Method(d.name, suffix);
+  }
+  std::string Method(const std::string &prefix, const StructDef &d) const {
+    return Method(prefix, d.name);
+  }
+  std::string Method(const std::string &prefix, const FieldDef &d) const {
+    return Method(prefix, d.name);
+  }
+  std::string Method(const std::string &prefix, const FieldDef &d,
+                     const std::string &suffix) const {
+    return Method(prefix, d.name, suffix);
+  }
+
+  std::string Namespace(const struct Namespace &ns) const {
+    return Namespace(ns.components);
+  }
+
+  std::string NamespacedEnumVariant(const EnumDef &e, const EnumVal &v) const {
+    return NamespacedString(e.defined_namespace, EnumVariant(e, v));
+  }
+
+  std::string NamespacedType(const Definition &def) const {
+    return NamespacedString(def.defined_namespace, Type(def.name));
+  }
+
+  std::string NamespacedObjectType(const Definition &def) const {
+    return NamespacedString(def.defined_namespace, ObjectType(def.name));
+  }
+
+  std::string Directories(const struct Namespace &ns,
+                          SkipDir skips = SkipDir::None) const {
+    return Directories(ns.components, skips);
+  }
+
+  // Legacy fields do not really follow the usual config and should be
+  // considered for deprecation.
+
+  std::string LegacyRustNativeVariant(const EnumVal &v) const {
+    return ConvertCase(EscapeKeyword(v.name), Case::kUpperCamel);
+  }
+
+  std::string LegacyRustFieldOffsetName(const FieldDef &field) const {
+    return "VT_" + ConvertCase(EscapeKeyword(field.name), Case::kAllUpper);
+  }
+
+  std::string LegacySwiftVariant(const EnumVal &ev) const {
+    auto name = ev.name;
+    if (isupper(name.front())) {
+      std::transform(name.begin(), name.end(), name.begin(), CharToLower);
+    }
+    return EscapeKeyword(ConvertCase(name, Case::kLowerCamel));
+  }
+
+  // Also used by Kotlin, lol.
+  std::string LegacyJavaMethod2(const std::string &prefix, const StructDef &sd,
+                                const std::string &suffix) const {
+    return prefix + sd.name + suffix;
+  }
+
+  std::string LegacyKotlinVariant(EnumVal &ev) const {
+    // Namer assumes the input case is snake case which is wrong...
+    return ConvertCase(EscapeKeyword(ev.name), Case::kLowerCamel);
+  }
+  // Kotlin methods escapes keywords after case conversion but before
+  // prefixing and suffixing.
+  std::string LegacyKotlinMethod(const std::string &prefix, const FieldDef &d,
+                                 const std::string &suffix) const {
+    return prefix + ConvertCase(EscapeKeyword(d.name), Case::kUpperCamel) +
+           suffix;
+  }
+  std::string LegacyKotlinMethod(const std::string &prefix, const StructDef &d,
+                                 const std::string &suffix) const {
+    return prefix + ConvertCase(EscapeKeyword(d.name), Case::kUpperCamel) +
+           suffix;
+  }
+
+  // This is a mix of snake case and keep casing, when Ts should be using
+  // lower camel case.
+  std::string LegacyTsMutateMethod(const FieldDef& d) {
+    return "mutate_" + d.name;
+  }
+
+ private:
+  std::string NamespacedString(const struct Namespace *ns,
+                               const std::string &str) const {
+    std::string ret;
+    if (ns != nullptr) { ret += Namespace(ns->components); }
+    if (!ret.empty()) ret += config_.namespace_seperator;
+    return ret + str;
+  }
+};
+
+// This is a temporary helper function for code generators to call until all
+// flag-overriding logic into flatc.cpp
+inline Namer::Config WithFlagOptions(const Namer::Config &input,
+                                     const IDLOptions &opts,
+                                     const std::string &path) {
+  Namer::Config result = input;
+  result.object_prefix = opts.object_prefix;
+  result.object_suffix = opts.object_suffix;
+  result.output_path = path;
+  result.filename_suffix = opts.filename_suffix;
+  return result;
+}
+
+}  // namespace flatbuffers
+
+#endif  // FLATBUFFERS_IDL_NAMER
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index dd106df..2d961b2 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -36,24 +36,9 @@
   // clang-format on
 }
 
-const double kPi = 3.14159265358979323846;
+namespace {
 
-// clang-format off
-const char *const kTypeNames[] = {
-  #define FLATBUFFERS_TD(ENUM, IDLTYPE, ...) \
-    IDLTYPE,
-    FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
-  #undef FLATBUFFERS_TD
-  nullptr
-};
-
-const char kTypeSizes[] = {
-  #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \
-    sizeof(CTYPE),
-    FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
-  #undef FLATBUFFERS_TD
-};
-// clang-format on
+static const double kPi = 3.14159265358979323846;
 
 // The enums in the reflection schema should match the ones we use internally.
 // Compare the last element to check if these go out of sync.
@@ -93,97 +78,36 @@
   return true;
 }
 
-void DeserializeDoc(std::vector<std::string> &doc,
-                    const Vector<Offset<String>> *documentation) {
+static void DeserializeDoc(std::vector<std::string> &doc,
+                           const Vector<Offset<String>> *documentation) {
   if (documentation == nullptr) return;
   for (uoffset_t index = 0; index < documentation->size(); index++)
     doc.push_back(documentation->Get(index)->str());
 }
 
-void Parser::Message(const std::string &msg) {
-  if (!error_.empty()) error_ += "\n";  // log all warnings and errors
-  error_ += file_being_parsed_.length() ? AbsolutePath(file_being_parsed_) : "";
-  // clang-format off
+static CheckedError NoError() { return CheckedError(false); }
 
-  #ifdef _WIN32  // MSVC alike
-    error_ +=
-        "(" + NumToString(line_) + ", " + NumToString(CursorPosition()) + ")";
-  #else  // gcc alike
-    if (file_being_parsed_.length()) error_ += ":";
-    error_ += NumToString(line_) + ": " + NumToString(CursorPosition());
-  #endif
-  // clang-format on
-  error_ += ": " + msg;
-}
-
-void Parser::Warning(const std::string &msg) {
-  if (!opts.no_warnings) {
-    Message("warning: " + msg);
-    has_warning_ = true;  // for opts.warnings_as_errors
-  }
-}
-
-CheckedError Parser::Error(const std::string &msg) {
-  Message("error: " + msg);
-  return CheckedError(true);
-}
-
-inline CheckedError NoError() { return CheckedError(false); }
-
-CheckedError Parser::RecurseError() {
-  return Error("maximum parsing depth " + NumToString(parse_depth_counter_) +
-               " reached");
-}
-
-const std::string &Parser::GetPooledString(const std::string &s) const {
-  return *(string_cache_.insert(s).first);
-}
-
-class Parser::ParseDepthGuard {
- public:
-  explicit ParseDepthGuard(Parser *parser_not_null)
-      : parser_(*parser_not_null), caller_depth_(parser_.parse_depth_counter_) {
-    FLATBUFFERS_ASSERT(caller_depth_ <= (FLATBUFFERS_MAX_PARSING_DEPTH) &&
-                       "Check() must be called to prevent stack overflow");
-    parser_.parse_depth_counter_ += 1;
-  }
-
-  ~ParseDepthGuard() { parser_.parse_depth_counter_ -= 1; }
-
-  CheckedError Check() {
-    return caller_depth_ >= (FLATBUFFERS_MAX_PARSING_DEPTH)
-               ? parser_.RecurseError()
-               : CheckedError(false);
-  }
-
-  FLATBUFFERS_DELETE_FUNC(ParseDepthGuard(const ParseDepthGuard &));
-  FLATBUFFERS_DELETE_FUNC(ParseDepthGuard &operator=(const ParseDepthGuard &));
-
- private:
-  Parser &parser_;
-  const int caller_depth_;
-};
-
-template<typename T> std::string TypeToIntervalString() {
+template<typename T> static std::string TypeToIntervalString() {
   return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " +
          NumToString((flatbuffers::numeric_limits<T>::max)()) + "]";
 }
 
 // atot: template version of atoi/atof: convert a string to an instance of T.
 template<typename T>
-bool atot_scalar(const char *s, T *val, bool_constant<false>) {
+static bool atot_scalar(const char *s, T *val, bool_constant<false>) {
   return StringToNumber(s, val);
 }
 
 template<typename T>
-bool atot_scalar(const char *s, T *val, bool_constant<true>) {
+static bool atot_scalar(const char *s, T *val, bool_constant<true>) {
   // Normalize NaN parsed from fbs or json to unsigned NaN.
   if (false == StringToNumber(s, val)) return false;
   *val = (*val != *val) ? std::fabs(*val) : *val;
   return true;
 }
 
-template<typename T> CheckedError atot(const char *s, Parser &parser, T *val) {
+template<typename T>
+static CheckedError atot(const char *s, Parser &parser, T *val) {
   auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>());
   if (done) return NoError();
   if (0 == *val)
@@ -193,33 +117,18 @@
                         ", constant does not fit " + TypeToIntervalString<T>());
 }
 template<>
-inline CheckedError atot<Offset<void>>(const char *s, Parser &parser,
-                                       Offset<void> *val) {
+CheckedError atot<Offset<void>>(const char *s, Parser &parser,
+                                Offset<void> *val) {
   (void)parser;
   *val = Offset<void>(atoi(s));
   return NoError();
 }
 
-std::string Namespace::GetFullyQualifiedName(const std::string &name,
-                                             size_t max_components) const {
-  // Early exit if we don't have a defined namespace.
-  if (components.empty() || !max_components) { return name; }
-  std::string stream_str;
-  for (size_t i = 0; i < std::min(components.size(), max_components); i++) {
-    stream_str += components[i];
-    stream_str += '.';
-  }
-  if (!stream_str.empty()) stream_str.pop_back();
-  if (name.length()) {
-    stream_str += '.';
-    stream_str += name;
-  }
-  return stream_str;
-}
-
 template<typename T>
-T *LookupTableByName(const SymbolTable<T> &table, const std::string &name,
-                     const Namespace &current_namespace, size_t skip_top) {
+static T *LookupTableByName(const SymbolTable<T> &table,
+                            const std::string &name,
+                            const Namespace &current_namespace,
+                            size_t skip_top) {
   const auto &components = current_namespace.components;
   if (table.dict.empty()) return nullptr;
   if (components.size() < skip_top) return nullptr;
@@ -278,6 +187,271 @@
 }
 // clang-format on
 
+static bool IsIdentifierStart(char c) { return is_alpha(c) || (c == '_'); }
+
+static bool CompareSerializedScalars(const uint8_t *a, const uint8_t *b,
+                                     const FieldDef &key) {
+  switch (key.value.type.base_type) {
+#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...)                       \
+  case BASE_TYPE_##ENUM: {                                              \
+    CTYPE def = static_cast<CTYPE>(0);                                  \
+    if (!a || !b) { StringToNumber(key.value.constant.c_str(), &def); } \
+    const auto av = a ? ReadScalar<CTYPE>(a) : def;                     \
+    const auto bv = b ? ReadScalar<CTYPE>(b) : def;                     \
+    return av < bv;                                                     \
+  }
+    FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
+#undef FLATBUFFERS_TD
+    default: {
+      FLATBUFFERS_ASSERT(false && "scalar type expected");
+      return false;
+    }
+  }
+}
+
+static bool CompareTablesByScalarKey(const Offset<Table> *_a,
+                                     const Offset<Table> *_b,
+                                     const FieldDef &key) {
+  const voffset_t offset = key.value.offset;
+  // Indirect offset pointer to table pointer.
+  auto a = reinterpret_cast<const uint8_t *>(_a) + ReadScalar<uoffset_t>(_a);
+  auto b = reinterpret_cast<const uint8_t *>(_b) + ReadScalar<uoffset_t>(_b);
+  // Fetch field address from table.
+  a = reinterpret_cast<const Table *>(a)->GetAddressOf(offset);
+  b = reinterpret_cast<const Table *>(b)->GetAddressOf(offset);
+  return CompareSerializedScalars(a, b, key);
+}
+
+static bool CompareTablesByStringKey(const Offset<Table> *_a,
+                                     const Offset<Table> *_b,
+                                     const FieldDef &key) {
+  const voffset_t offset = key.value.offset;
+  // Indirect offset pointer to table pointer.
+  auto a = reinterpret_cast<const uint8_t *>(_a) + ReadScalar<uoffset_t>(_a);
+  auto b = reinterpret_cast<const uint8_t *>(_b) + ReadScalar<uoffset_t>(_b);
+  // Fetch field address from table.
+  a = reinterpret_cast<const Table *>(a)->GetAddressOf(offset);
+  b = reinterpret_cast<const Table *>(b)->GetAddressOf(offset);
+  if (a && b) {
+    // Indirect offset pointer to string pointer.
+    a += ReadScalar<uoffset_t>(a);
+    b += ReadScalar<uoffset_t>(b);
+    return *reinterpret_cast<const String *>(a) <
+           *reinterpret_cast<const String *>(b);
+  } else {
+    return a ? true : false;
+  }
+}
+
+static void SwapSerializedTables(Offset<Table> *a, Offset<Table> *b) {
+  // These are serialized offsets, so are relative where they are
+  // stored in memory, so compute the distance between these pointers:
+  ptrdiff_t diff = (b - a) * sizeof(Offset<Table>);
+  FLATBUFFERS_ASSERT(diff >= 0);  // Guaranteed by SimpleQsort.
+  auto udiff = static_cast<uoffset_t>(diff);
+  a->o = EndianScalar(ReadScalar<uoffset_t>(a) - udiff);
+  b->o = EndianScalar(ReadScalar<uoffset_t>(b) + udiff);
+  std::swap(*a, *b);
+}
+
+// See below for why we need our own sort :(
+template<typename T, typename F, typename S>
+static void SimpleQsort(T *begin, T *end, size_t width, F comparator,
+                        S swapper) {
+  if (end - begin <= static_cast<ptrdiff_t>(width)) return;
+  auto l = begin + width;
+  auto r = end;
+  while (l < r) {
+    if (comparator(begin, l)) {
+      r -= width;
+      swapper(l, r);
+    } else {
+      l += width;
+    }
+  }
+  l -= width;
+  swapper(begin, l);
+  SimpleQsort(begin, l, width, comparator, swapper);
+  SimpleQsort(r, end, width, comparator, swapper);
+}
+
+template<typename T> static inline void SingleValueRepack(Value &e, T val) {
+  // Remove leading zeros.
+  if (IsInteger(e.type.base_type)) { e.constant = NumToString(val); }
+}
+
+#if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
+// Normalize defaults NaN to unsigned quiet-NaN(0) if value was parsed from
+// hex-float literal.
+static void SingleValueRepack(Value &e, float val) {
+  if (val != val) e.constant = "nan";
+}
+static void SingleValueRepack(Value &e, double val) {
+  if (val != val) e.constant = "nan";
+}
+#endif
+
+template<typename T> static uint64_t EnumDistanceImpl(T e1, T e2) {
+  if (e1 < e2) { std::swap(e1, e2); }  // use std for scalars
+  // Signed overflow may occur, use unsigned calculation.
+  // The unsigned overflow is well-defined by C++ standard (modulo 2^n).
+  return static_cast<uint64_t>(e1) - static_cast<uint64_t>(e2);
+}
+
+static bool compareFieldDefs(const FieldDef *a, const FieldDef *b) {
+  auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
+  auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
+  return a_id < b_id;
+}
+
+static Namespace *GetNamespace(
+    const std::string &qualified_name, std::vector<Namespace *> &namespaces,
+    std::map<std::string, Namespace *> &namespaces_index) {
+  size_t dot = qualified_name.find_last_of('.');
+  std::string namespace_name = (dot != std::string::npos)
+                                   ? std::string(qualified_name.c_str(), dot)
+                                   : "";
+  Namespace *&ns = namespaces_index[namespace_name];
+
+  if (!ns) {
+    ns = new Namespace();
+    namespaces.push_back(ns);
+
+    size_t pos = 0;
+
+    for (;;) {
+      dot = qualified_name.find('.', pos);
+      if (dot == std::string::npos) { break; }
+      ns->components.push_back(qualified_name.substr(pos, dot - pos));
+      pos = dot + 1;
+    }
+  }
+
+  return ns;
+}
+
+// Generate a unique hash for a file based on its name and contents (if any).
+static uint64_t HashFile(const char *source_filename, const char *source) {
+  uint64_t hash = 0;
+
+  if (source_filename)
+    hash = HashFnv1a<uint64_t>(StripPath(source_filename).c_str());
+
+  if (source && *source) hash ^= HashFnv1a<uint64_t>(source);
+
+  return hash;
+}
+
+template<typename T> static bool compareName(const T *a, const T *b) {
+  return a->defined_namespace->GetFullyQualifiedName(a->name) <
+         b->defined_namespace->GetFullyQualifiedName(b->name);
+}
+
+template<typename T> static void AssignIndices(const std::vector<T *> &defvec) {
+  // Pre-sort these vectors, such that we can set the correct indices for them.
+  auto vec = defvec;
+  std::sort(vec.begin(), vec.end(), compareName<T>);
+  for (int i = 0; i < static_cast<int>(vec.size()); i++) vec[i]->index = i;
+}
+
+}  // namespace
+
+// clang-format off
+const char *const kTypeNames[] = {
+  #define FLATBUFFERS_TD(ENUM, IDLTYPE, ...) \
+    IDLTYPE,
+    FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+  #undef FLATBUFFERS_TD
+  nullptr
+};
+
+const char kTypeSizes[] = {
+  #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \
+    sizeof(CTYPE),
+    FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+  #undef FLATBUFFERS_TD
+};
+// clang-format on
+
+void Parser::Message(const std::string &msg) {
+  if (!error_.empty()) error_ += "\n";  // log all warnings and errors
+  error_ += file_being_parsed_.length() ? AbsolutePath(file_being_parsed_) : "";
+  // clang-format off
+
+  #ifdef _WIN32  // MSVC alike
+    error_ +=
+        "(" + NumToString(line_) + ", " + NumToString(CursorPosition()) + ")";
+  #else  // gcc alike
+    if (file_being_parsed_.length()) error_ += ":";
+    error_ += NumToString(line_) + ": " + NumToString(CursorPosition());
+  #endif
+  // clang-format on
+  error_ += ": " + msg;
+}
+
+void Parser::Warning(const std::string &msg) {
+  if (!opts.no_warnings) {
+    Message("warning: " + msg);
+    has_warning_ = true;  // for opts.warnings_as_errors
+  }
+}
+
+CheckedError Parser::Error(const std::string &msg) {
+  Message("error: " + msg);
+  return CheckedError(true);
+}
+
+CheckedError Parser::RecurseError() {
+  return Error("maximum parsing depth " + NumToString(parse_depth_counter_) +
+               " reached");
+}
+
+const std::string &Parser::GetPooledString(const std::string &s) const {
+  return *(string_cache_.insert(s).first);
+}
+
+class Parser::ParseDepthGuard {
+ public:
+  explicit ParseDepthGuard(Parser *parser_not_null)
+      : parser_(*parser_not_null), caller_depth_(parser_.parse_depth_counter_) {
+    FLATBUFFERS_ASSERT(caller_depth_ <= (FLATBUFFERS_MAX_PARSING_DEPTH) &&
+                       "Check() must be called to prevent stack overflow");
+    parser_.parse_depth_counter_ += 1;
+  }
+
+  ~ParseDepthGuard() { parser_.parse_depth_counter_ -= 1; }
+
+  CheckedError Check() {
+    return caller_depth_ >= (FLATBUFFERS_MAX_PARSING_DEPTH)
+               ? parser_.RecurseError()
+               : CheckedError(false);
+  }
+
+  FLATBUFFERS_DELETE_FUNC(ParseDepthGuard(const ParseDepthGuard &));
+  FLATBUFFERS_DELETE_FUNC(ParseDepthGuard &operator=(const ParseDepthGuard &));
+
+ private:
+  Parser &parser_;
+  const int caller_depth_;
+};
+
+std::string Namespace::GetFullyQualifiedName(const std::string &name,
+                                             size_t max_components) const {
+  // Early exit if we don't have a defined namespace.
+  if (components.empty() || !max_components) { return name; }
+  std::string stream_str;
+  for (size_t i = 0; i < std::min(components.size(), max_components); i++) {
+    stream_str += components[i];
+    stream_str += '.';
+  }
+  if (!stream_str.empty()) stream_str.pop_back();
+  if (name.length()) {
+    stream_str += '.';
+    stream_str += name;
+  }
+  return stream_str;
+}
+
 std::string Parser::TokenToStringId(int t) const {
   return t == kTokenIdentifier ? attribute_ : TokenToString(t);
 }
@@ -307,10 +481,6 @@
   return NoError();
 }
 
-static inline bool IsIdentifierStart(char c) {
-  return is_alpha(c) || (c == '_');
-}
-
 CheckedError Parser::Next() {
   doc_comment_.clear();
   bool seen_newline = cursor_ == source_;
@@ -489,10 +659,21 @@
         }
 
         const auto has_sign = (c == '+') || (c == '-');
-        if (has_sign && IsIdentifierStart(*cursor_)) {
-          // '-'/'+' and following identifier - it could be a predefined
-          // constant. Return the sign in token_, see ParseSingleValue.
-          return NoError();
+        if (has_sign) {
+          // Check for +/-inf which is considered a float constant.
+          if (strncmp(cursor_, "inf", 3) == 0 &&
+              !(IsIdentifierStart(cursor_[3]) || is_digit(cursor_[3]))) {
+            attribute_.assign(cursor_ - 1, cursor_ + 3);
+            token_ = kTokenFloatConstant;
+            cursor_ += 3;
+            return NoError();
+          }
+
+          if (IsIdentifierStart(*cursor_)) {
+            // '-'/'+' and following identifier - it could be a predefined
+            // constant. Return the sign in token_, see ParseSingleValue.
+            return NoError();
+          }
         }
 
         auto dot_lvl =
@@ -958,8 +1139,12 @@
         "definition");
 
   field->native_inline = field->attributes.Lookup("native_inline") != nullptr;
-  if (field->native_inline && !IsStruct(field->value.type))
-    return Error("native_inline can only be defined on structs");
+  if (field->native_inline && !IsStruct(field->value.type) &&
+      !IsVectorOfStruct(field->value.type) &&
+      !IsVectorOfTable(field->value.type))
+    return Error(
+        "'native_inline' can only be defined on structs, vector of structs or "
+        "vector of tables");
 
   auto nested = field->attributes.Lookup("nested_flatbuffer");
   if (nested) {
@@ -1102,8 +1287,9 @@
       uint8_t enum_idx;
       if (vector_of_union_types) {
         if (vector_of_union_types->size() <= count)
-          return Error("union types vector smaller than union values vector"
-                       " for: " + field->name);
+          return Error(
+              "union types vector smaller than union values vector for: " +
+              field->name);
         enum_idx = vector_of_union_types->Get(count);
       } else {
         ECHECK(atot(constant.c_str(), *this, &enum_idx));
@@ -1323,10 +1509,18 @@
                 ECHECK(atot(field_value.constant.c_str(), *this, &val)); \
                 builder_.PushElement(val); \
               } else { \
-                CTYPE val, valdef; \
-                ECHECK(atot(field_value.constant.c_str(), *this, &val)); \
-                ECHECK(atot(field->value.constant.c_str(), *this, &valdef)); \
-                builder_.AddElement(field_value.offset, val, valdef); \
+                if (field->IsScalarOptional()) { \
+                  if (field_value.constant != "null") { \
+                    CTYPE val; \
+                    ECHECK(atot(field_value.constant.c_str(), *this, &val)); \
+                    builder_.AddElement(field_value.offset, val); \
+                  } \
+                } else { \
+                  CTYPE val, valdef; \
+                  ECHECK(atot(field_value.constant.c_str(), *this, &val)); \
+                  ECHECK(atot(field->value.constant.c_str(), *this, &valdef)); \
+                  builder_.AddElement(field_value.offset, val, valdef); \
+                } \
               } \
               break;
             FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
@@ -1390,90 +1584,6 @@
   return NoError();
 }
 
-static bool CompareSerializedScalars(const uint8_t *a, const uint8_t *b,
-                                     const FieldDef &key) {
-  switch (key.value.type.base_type) {
-#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...)                       \
-  case BASE_TYPE_##ENUM: {                                              \
-    CTYPE def = static_cast<CTYPE>(0);                                  \
-    if (!a || !b) { StringToNumber(key.value.constant.c_str(), &def); } \
-    const auto av = a ? ReadScalar<CTYPE>(a) : def;                     \
-    const auto bv = b ? ReadScalar<CTYPE>(b) : def;                     \
-    return av < bv;                                                     \
-  }
-    FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
-#undef FLATBUFFERS_TD
-    default: {
-      FLATBUFFERS_ASSERT(false && "scalar type expected");
-      return false;
-    }
-  }
-}
-
-static bool CompareTablesByScalarKey(const Offset<Table> *_a,
-                                     const Offset<Table> *_b,
-                                     const FieldDef &key) {
-  const voffset_t offset = key.value.offset;
-  // Indirect offset pointer to table pointer.
-  auto a = reinterpret_cast<const uint8_t *>(_a) + ReadScalar<uoffset_t>(_a);
-  auto b = reinterpret_cast<const uint8_t *>(_b) + ReadScalar<uoffset_t>(_b);
-  // Fetch field address from table.
-  a = reinterpret_cast<const Table *>(a)->GetAddressOf(offset);
-  b = reinterpret_cast<const Table *>(b)->GetAddressOf(offset);
-  return CompareSerializedScalars(a, b, key);
-}
-
-static bool CompareTablesByStringKey(const Offset<Table> *_a,
-                                     const Offset<Table> *_b,
-                                     const FieldDef &key) {
-  const voffset_t offset = key.value.offset;
-  // Indirect offset pointer to table pointer.
-  auto a = reinterpret_cast<const uint8_t *>(_a) + ReadScalar<uoffset_t>(_a);
-  auto b = reinterpret_cast<const uint8_t *>(_b) + ReadScalar<uoffset_t>(_b);
-  // Fetch field address from table.
-  a = reinterpret_cast<const Table *>(a)->GetAddressOf(offset);
-  b = reinterpret_cast<const Table *>(b)->GetAddressOf(offset);
-  if (a && b) {
-    // Indirect offset pointer to string pointer.
-    a += ReadScalar<uoffset_t>(a);
-    b += ReadScalar<uoffset_t>(b);
-    return *reinterpret_cast<const String *>(a) <
-           *reinterpret_cast<const String *>(b);
-  } else {
-    return a ? true : false;
-  }
-}
-
-static void SwapSerializedTables(Offset<Table> *a, Offset<Table> *b) {
-  // These are serialized offsets, so are relative where they are
-  // stored in memory, so compute the distance between these pointers:
-  ptrdiff_t diff = (b - a) * sizeof(Offset<Table>);
-  FLATBUFFERS_ASSERT(diff >= 0);  // Guaranteed by SimpleQsort.
-  auto udiff = static_cast<uoffset_t>(diff);
-  a->o = EndianScalar(ReadScalar<uoffset_t>(a) - udiff);
-  b->o = EndianScalar(ReadScalar<uoffset_t>(b) + udiff);
-  std::swap(*a, *b);
-}
-
-// See below for why we need our own sort :(
-template<typename T, typename F, typename S>
-void SimpleQsort(T *begin, T *end, size_t width, F comparator, S swapper) {
-  if (end - begin <= static_cast<ptrdiff_t>(width)) return;
-  auto l = begin + width;
-  auto r = end;
-  while (l < r) {
-    if (comparator(begin, l)) {
-      r -= width;
-      swapper(l, r);
-    } else {
-      l += width;
-    }
-  }
-  l -= width;
-  swapper(begin, l);
-  SimpleQsort(begin, l, width, comparator, swapper);
-  SimpleQsort(r, end, width, comparator, swapper);
-}
 
 CheckedError Parser::ParseAlignAttribute(const std::string &align_constant,
                                          size_t min_align, size_t *align) {
@@ -1801,22 +1911,6 @@
   return Error("cannot parse value starting with: " + TokenToStringId(token_));
 }
 
-// Re-pack helper (ParseSingleValue) to normalize defaults of scalars.
-template<typename T> inline void SingleValueRepack(Value &e, T val) {
-  // Remove leading zeros.
-  if (IsInteger(e.type.base_type)) { e.constant = NumToString(val); }
-}
-#if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
-// Normalize defaults NaN to unsigned quiet-NaN(0) if value was parsed from
-// hex-float literal.
-static inline void SingleValueRepack(Value &e, float val) {
-  if (val != val) e.constant = "nan";
-}
-static inline void SingleValueRepack(Value &e, double val) {
-  if (val != val) e.constant = "nan";
-}
-#endif
-
 CheckedError Parser::ParseFunction(const std::string *name, Value &e) {
   ParseDepthGuard depth_guard(this);
   ECHECK(depth_guard.Check());
@@ -2088,13 +2182,6 @@
   return vals.vec.empty() ? nullptr : vals.vec.back();
 }
 
-template<typename T> static uint64_t EnumDistanceImpl(T e1, T e2) {
-  if (e1 < e2) { std::swap(e1, e2); }  // use std for scalars
-  // Signed overflow may occur, use unsigned calculation.
-  // The unsigned overflow is well-defined by C++ standard (modulo 2^n).
-  return static_cast<uint64_t>(e1) - static_cast<uint64_t>(e2);
-}
-
 uint64_t EnumDef::Distance(const EnumVal *v1, const EnumVal *v2) const {
   return IsUInt64() ? EnumDistanceImpl(v1->GetAsUInt64(), v2->GetAsUInt64())
                     : EnumDistanceImpl(v1->GetAsInt64(), v2->GetAsInt64());
@@ -2312,6 +2399,9 @@
     // todo: Convert to the Error in the future?
     Warning("underlying type of bit_flags enum must be unsigned");
   }
+  if (enum_def->attributes.Lookup("force_align")) {
+    return Error("`force_align` is not a valid attribute for Enums. ");
+  }
   EnumValBuilder evb(*this, *enum_def);
   EXPECT('{');
   // A lot of code generatos expect that an enum is not-empty.
@@ -2448,12 +2538,19 @@
   return NoError();
 }
 
+std::vector<IncludedFile> Parser::GetIncludedFiles() const {
+  const auto it = files_included_per_file_.find(file_being_parsed_);
+  if (it == files_included_per_file_.end()) { return {}; }
+
+  return { it->second.cbegin(), it->second.cend() };
+}
+
 bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions &opts) {
   static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
       IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster |
       IDLOptions::kKotlin | IDLOptions::kCpp | IDLOptions::kJava |
       IDLOptions::kCSharp | IDLOptions::kTs | IDLOptions::kBinary |
-      IDLOptions::kGo;
+      IDLOptions::kGo | IDLOptions::kPython | IDLOptions::kJson;
   unsigned long langs = opts.lang_to_generate;
   return (langs > 0 && langs < IDLOptions::kMAX) && !(langs & ~supported_langs);
 }
@@ -2508,12 +2605,6 @@
   return full_qualified_name.substr(previous, current - previous);
 }
 
-static bool compareFieldDefs(const FieldDef *a, const FieldDef *b) {
-  auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
-  auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
-  return a_id < b_id;
-}
-
 CheckedError Parser::ParseDecl(const char *filename) {
   std::vector<std::string> dc = doc_comment_;
   bool fixed = IsIdent("struct");
@@ -2884,7 +2975,11 @@
           if (key == "default") {
             // Temp: skip non-numeric and non-boolean defaults (enums).
             auto numeric = strpbrk(val.c_str(), "0123456789-+.");
-            if (IsScalar(type.base_type) && numeric == val.c_str()) {
+            if (IsFloat(type.base_type) &&
+                (val == "inf" || val == "+inf" || val == "-inf")) {
+              // Prefer to be explicit with +inf.
+              field->value.constant = val == "inf" ? "+inf" : val;
+            } else if (IsScalar(type.base_type) && numeric == val.c_str()) {
               field->value.constant = val;
             } else if (val == "true") {
               field->value.constant = val;
@@ -3032,7 +3127,8 @@
     case kTokenIntegerConstant:
     case kTokenFloatConstant: NEXT(); break;
     default:
-      if (IsIdent("true") || IsIdent("false") || IsIdent("null")) {
+      if (IsIdent("true") || IsIdent("false") || IsIdent("null") ||
+          IsIdent("inf")) {
         NEXT();
       } else
         return TokenError();
@@ -3245,32 +3341,79 @@
       for (auto val_it = enum_def.Vals().begin();
            val_it != enum_def.Vals().end(); ++val_it) {
         auto &val = **val_it;
+
         if (!(opts.lang_to_generate != 0 && SupportsAdvancedUnionFeatures()) &&
             (IsStruct(val.union_type) || IsString(val.union_type)))
+
           return Error(
               "only tables can be union elements in the generated language: " +
               val.name);
       }
     }
   }
+
+  auto err = CheckPrivateLeak();
+  if (err.Check()) return err;
+
   // Parse JSON object only if the scheme has been parsed.
   if (token_ == '{') { ECHECK(DoParseJson()); }
-  EXPECT(kTokenEof);
   return NoError();
 }
 
-// Generate a unique hash for a file based on its name and contents (if any).
-static uint64_t HashFile(const char *source_filename, const char *source) {
-  uint64_t hash = 0;
+CheckedError Parser::CheckPrivateLeak() {
+  if (!opts.no_leak_private_annotations) return NoError();
+  // Iterate over all structs/tables to validate we arent leaking
+  // any private (structs/tables/enums)
+  for (auto it = structs_.vec.begin(); it != structs_.vec.end(); it++) {
+    auto &struct_def = **it;
+    for (auto fld_it = struct_def.fields.vec.begin();
+         fld_it != struct_def.fields.vec.end(); ++fld_it) {
+      auto &field = **fld_it;
 
-  if (source_filename)
-    hash = HashFnv1a<uint64_t>(StripPath(source_filename).c_str());
-
-  if (source && *source) hash ^= HashFnv1a<uint64_t>(source);
-
-  return hash;
+      if (field.value.type.enum_def) {
+        auto err =
+            CheckPrivatelyLeakedFields(struct_def, *field.value.type.enum_def);
+        if (err.Check()) { return err; }
+      } else if (field.value.type.struct_def) {
+        auto err = CheckPrivatelyLeakedFields(struct_def,
+                                              *field.value.type.struct_def);
+        if (err.Check()) { return err; }
+      }
+    }
+  }
+  // Iterate over all enums to validate we arent leaking
+  // any private (structs/tables)
+  for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
+    auto &enum_def = **it;
+    if (enum_def.is_union) {
+      for (auto val_it = enum_def.Vals().begin();
+           val_it != enum_def.Vals().end(); ++val_it) {
+        auto &val = **val_it;
+        if (val.union_type.struct_def) {
+          auto err =
+              CheckPrivatelyLeakedFields(enum_def, *val.union_type.struct_def);
+          if (err.Check()) { return err; }
+        }
+      }
+    }
+  }
+  return NoError();
 }
 
+CheckedError Parser::CheckPrivatelyLeakedFields(const Definition &def,
+                                                const Definition &value_type) {
+  if (!opts.no_leak_private_annotations) return NoError();
+  const auto is_private = def.attributes.Lookup("private");
+  const auto is_field_private = value_type.attributes.Lookup("private");
+  if (!is_private && is_field_private) {
+    return Error(
+        "Leaking private implementation, verify all objects have similar "
+        "annotations");
+  }
+  return NoError();
+}
+
+
 CheckedError Parser::DoParse(const char *source, const char **include_paths,
                              const char *source_filename,
                              const char *include_filename) {
@@ -3285,7 +3428,7 @@
 
     if (included_files_.find(source_hash) == included_files_.end()) {
       included_files_[source_hash] = include_filename ? include_filename : "";
-      files_included_per_file_[source_filename] = std::set<std::string>();
+      files_included_per_file_[source_filename] = std::set<IncludedFile>();
     } else {
       return NoError();
     }
@@ -3333,8 +3476,12 @@
       }
       if (filepath.empty())
         return Error("unable to locate include file: " + name);
-      if (source_filename)
-        files_included_per_file_[source_filename].insert(filepath);
+      if (source_filename) {
+        IncludedFile included_file;
+        included_file.filename = filepath;
+        included_file.schema_name = name;
+        files_included_per_file_[source_filename].insert(included_file);
+      }
 
       std::string contents;
       bool file_loaded = LoadFile(filepath.c_str(), true, &contents);
@@ -3423,6 +3570,7 @@
       ECHECK(ParseDecl(source_filename));
     }
   }
+  EXPECT(kTokenEof);
   if (opts.warnings_as_errors && has_warning_) {
     return Error("treating warnings as errors, failed due to above warnings");
   }
@@ -3470,11 +3618,11 @@
 
     // Workaround the lack of const accessor in C++98 maps.
     auto &new_files =
-        (*const_cast<std::map<std::string, std::set<std::string>> *>(
+        (*const_cast<std::map<std::string, std::set<IncludedFile>> *>(
             &files_included_per_file_))[current];
     for (auto it = new_files.begin(); it != new_files.end(); ++it) {
-      if (included_files.find(*it) == included_files.end())
-        to_process.push_back(*it);
+      if (included_files.find(it->filename) == included_files.end())
+        to_process.push_back(it->filename);
     }
   }
 
@@ -3483,18 +3631,6 @@
 
 // Schema serialization functionality:
 
-template<typename T> bool compareName(const T *a, const T *b) {
-  return a->defined_namespace->GetFullyQualifiedName(a->name) <
-         b->defined_namespace->GetFullyQualifiedName(b->name);
-}
-
-template<typename T> void AssignIndices(const std::vector<T *> &defvec) {
-  // Pre-sort these vectors, such that we can set the correct indices for them.
-  auto vec = defvec;
-  std::sort(vec.begin(), vec.end(), compareName<T>);
-  for (int i = 0; i < static_cast<int>(vec.size()); i++) vec[i]->index = i;
-}
-
 void Parser::Serialize() {
   builder_.Clear();
   AssignIndices(structs_.vec);
@@ -3536,7 +3672,7 @@
           RelativeToRootPath(opts.project_root, f->first));
       for (auto i = f->second.begin(); i != f->second.end(); i++) {
         included_files.push_back(builder_.CreateSharedString(
-            RelativeToRootPath(opts.project_root, *i)));
+            RelativeToRootPath(opts.project_root, i->filename)));
       }
       const auto included_files__ = builder_.CreateVector(included_files);
       included_files.clear();
@@ -3564,32 +3700,6 @@
   }
 }
 
-static Namespace *GetNamespace(
-    const std::string &qualified_name, std::vector<Namespace *> &namespaces,
-    std::map<std::string, Namespace *> &namespaces_index) {
-  size_t dot = qualified_name.find_last_of('.');
-  std::string namespace_name = (dot != std::string::npos)
-                                   ? std::string(qualified_name.c_str(), dot)
-                                   : "";
-  Namespace *&ns = namespaces_index[namespace_name];
-
-  if (!ns) {
-    ns = new Namespace();
-    namespaces.push_back(ns);
-
-    size_t pos = 0;
-
-    for (;;) {
-      dot = qualified_name.find('.', pos);
-      if (dot == std::string::npos) { break; }
-      ns->components.push_back(qualified_name.substr(pos, dot - pos));
-      pos = dot + 1;
-    }
-  }
-
-  return ns;
-}
-
 Offset<reflection::Object> StructDef::Serialize(FlatBufferBuilder *builder,
                                                 const Parser &parser) const {
   std::vector<Offset<reflection::Field>> field_offsets;
@@ -3629,6 +3739,14 @@
       delete field_def;
       return false;
     }
+    if (field_def->key) {
+      if (has_key) {
+        // only one field may be set as key
+        delete field_def;
+        return false;
+      }
+      has_key = true;
+    }
     if (fixed) {
       // Recompute padding since that's currently not serialized.
       auto size = InlineSize(field_def->value.type);
@@ -3986,7 +4104,9 @@
          ++s) {
       for (auto f = s->included_filenames()->begin();
            f != s->included_filenames()->end(); ++f) {
-        files_included_per_file_[s->filename()->str()].insert(f->str());
+        IncludedFile included_file;
+        included_file.filename = f->str();
+        files_included_per_file_[s->filename()->str()].insert(included_file);
       }
     }
 
diff --git a/src/namer.h b/src/namer.h
index 6c539cb..8fd8354 100644
--- a/src/namer.h
+++ b/src/namer.h
@@ -1,7 +1,6 @@
 #ifndef FLATBUFFERS_NAMER
 #define FLATBUFFERS_NAMER
 
-#include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
 
 namespace flatbuffers {
@@ -62,6 +61,16 @@
     // e.g. `Enum::MyVariant` uses `::`.
     std::string enum_variant_seperator;
 
+    // Configures, when formatting code, whether symbols are checked against
+    // keywords and escaped before or after case conversion. It does not make
+    // sense to do so before, but its legacy behavior. :shrug:
+    // TODO(caspern): Deprecate.
+    enum class Escape {
+      BeforeConvertingCase,
+      AfterConvertingCase,
+    };
+    Escape escape_keywords;
+
     // Namespaces
 
     // e.g. `namespace my_namespace {}`
@@ -95,64 +104,55 @@
     std::string filename_suffix;
     // Extension for generated files, e.g. ".cpp" or ".rs".
     std::string filename_extension;
-
-    // This is a temporary helper function for code generators to call until all
-    // code generators are using `Namer`. After that point, we can centralize
-    // flag-overriding logic into flatc.cpp
-    Config WithFlagOptions(const IDLOptions &opts,
-                           const std::string &path) const {
-      Config result = *this;
-      result.object_prefix = opts.object_prefix;
-      result.object_suffix = opts.object_suffix;
-      result.output_path = path;
-      result.filename_suffix = opts.filename_suffix;
-      return result;
-    }
   };
   Namer(Config config, std::set<std::string> keywords)
       : config_(config), keywords_(std::move(keywords)) {}
 
-  std::string Type(const std::string &s) const {
-    return Format(s, config_.types);
+  virtual ~Namer() {}
+
+  template<typename T> std::string Method(const T &s) const {
+    return Method(s.name);
   }
 
-  std::string Method(const std::string &s) const {
+  virtual std::string Method(const std::string &pre,
+                             const std::string &mid,
+                             const std::string &suf) const {
+    return Format(pre + "_" +  mid + "_" + suf, config_.methods);
+  }
+  virtual std::string Method(const std::string &pre,
+                             const std::string &suf) const {
+    return Format(pre + "_" + suf, config_.methods);
+  }
+  virtual std::string Method(const std::string &s) const {
     return Format(s, config_.methods);
   }
 
-  std::string Constant(const std::string &s) const {
+  virtual std::string Constant(const std::string &s) const {
     return Format(s, config_.constants);
   }
 
-  std::string Function(const std::string &s) const {
+  virtual std::string Function(const std::string &s) const {
     return Format(s, config_.functions);
   }
 
-  std::string Field(const std::string &s) const {
-    return Format(s, config_.fields);
-  }
-
-  std::string Variable(const std::string &s) const {
+  virtual std::string Variable(const std::string &s) const {
     return Format(s, config_.variables);
   }
 
-  std::string Variant(const std::string &s) const {
-    return Format(s, config_.variants);
+  template<typename T>
+  std::string Variable(const std::string &p, const T &s) const {
+    return Format(p + "_" + s.name, config_.variables);
+  }
+  virtual std::string Variable(const std::string &p,
+                               const std::string &s) const {
+    return Format(p + "_" + s, config_.variables);
   }
 
-  std::string EnumVariant(const std::string &e, const std::string v) const {
-    return Type(e) + config_.enum_variant_seperator + Variant(v);
-  }
-
-  std::string ObjectType(const std::string &s) const {
-    return config_.object_prefix + Type(s) + config_.object_suffix;
-  }
-
-  std::string Namespace(const std::string &s) const {
+  virtual std::string Namespace(const std::string &s) const {
     return Format(s, config_.namespaces);
   }
 
-  std::string Namespace(const std::vector<std::string> &ns) const {
+  virtual std::string Namespace(const std::vector<std::string> &ns) const {
     std::string result;
     for (auto it = ns.begin(); it != ns.end(); it++) {
       if (it != ns.begin()) result += config_.namespace_seperator;
@@ -161,26 +161,32 @@
     return result;
   }
 
-  std::string NamespacedType(const std::vector<std::string> &ns,
-                             const std::string &s) const {
-    return Namespace(ns) + config_.namespace_seperator + Type(s);
+  virtual std::string NamespacedType(const std::vector<std::string> &ns,
+                                     const std::string &s) const {
+    return (ns.empty() ? "" : (Namespace(ns) + config_.namespace_seperator)) +
+           Type(s);
   }
 
   // Returns `filename` with the right casing, suffix, and extension.
-  std::string File(const std::string &filename,
-                   SkipFile skips = SkipFile::None) const {
+  virtual std::string File(const std::string &filename,
+                           SkipFile skips = SkipFile::None) const {
     const bool skip_suffix = (skips & SkipFile::Suffix) != SkipFile::None;
     const bool skip_ext = (skips & SkipFile::Extension) != SkipFile::None;
     return ConvertCase(filename, config_.filenames, Case::kUpperCamel) +
            (skip_suffix ? "" : config_.filename_suffix) +
            (skip_ext ? "" : config_.filename_extension);
   }
+  template<typename T>
+  std::string File(const T &f, SkipFile skips = SkipFile::None) const {
+    return File(f.name, skips);
+  }
+
   // Formats `directories` prefixed with the output_path and joined with the
   // right seperator. Output path prefixing and the trailing separator may be
   // skiped using `skips`.
   // Callers may want to use `EnsureDirExists` with the result.
-  std::string Directories(const std::vector<std::string> &directories,
-                          SkipDir skips = SkipDir::None) const {
+  virtual std::string Directories(const std::vector<std::string> &directories,
+                                  SkipDir skips = SkipDir::None) const {
     const bool skip_output_path =
         (skips & SkipDir::OutputPath) != SkipDir::None;
     const bool skip_trailing_seperator =
@@ -194,7 +200,7 @@
     return result;
   }
 
-  std::string EscapeKeyword(const std::string &name) const {
+  virtual std::string EscapeKeyword(const std::string &name) const {
     if (keywords_.find(name) == keywords_.end()) {
       return name;
     } else {
@@ -202,12 +208,55 @@
     }
   }
 
- private:
-  std::string Format(const std::string &s, Case casing) const {
-    // NOTE: If you need to escape keywords after converting case, which would
-    // make more sense than this, make it a config option.
-    return ConvertCase(EscapeKeyword(s), casing, Case::kLowerCamel);
+  virtual std::string Type(const std::string &s) const {
+    return Format(s, config_.types);
   }
+  virtual std::string Type(const std::string &t, const std::string &s) const {
+    return Format(t + "_" + s, config_.types);
+  }
+
+  virtual std::string ObjectType(const std::string &s) const {
+    return config_.object_prefix + Type(s) + config_.object_suffix;
+  }
+
+  virtual std::string Field(const std::string &s) const {
+    return Format(s, config_.fields);
+  }
+
+  virtual std::string Variant(const std::string &s) const {
+    return Format(s, config_.variants);
+  }
+
+  virtual std::string Format(const std::string &s, Case casing) const {
+    if (config_.escape_keywords == Config::Escape::BeforeConvertingCase) {
+      return ConvertCase(EscapeKeyword(s), casing, Case::kLowerCamel);
+    } else {
+      return EscapeKeyword(ConvertCase(s, casing, Case::kLowerCamel));
+    }
+  }
+
+  // Denamespaces a string (e.g. The.Quick.Brown.Fox) by returning the last part
+  // after the `delimiter` (Fox) and placing the rest in `namespace_prefix`
+  // (The.Quick.Brown).
+  virtual std::string Denamespace(const std::string &s,
+                                  std::string &namespace_prefix,
+                                  const char delimiter = '.') const {
+    const size_t pos = s.find_last_of(delimiter);
+    if (pos == std::string::npos) {
+      namespace_prefix = "";
+      return s;
+    }
+    namespace_prefix = s.substr(0, pos);
+    return s.substr(pos + 1);
+  }
+
+  // Same as above, but disregards the prefix.
+  virtual std::string Denamespace(const std::string &s,
+                                  const char delimiter = '.') const {
+    std::string prefix;
+    return Denamespace(s, prefix, delimiter);
+  }
+
   const Config config_;
   const std::set<std::string> keywords_;
 };
diff --git a/src/reflection.cpp b/src/reflection.cpp
index 6bba28c..9abfd9f 100644
--- a/src/reflection.cpp
+++ b/src/reflection.cpp
@@ -22,6 +22,238 @@
 
 namespace flatbuffers {
 
+namespace {
+
+static void CopyInline(FlatBufferBuilder &fbb, const reflection::Field &fielddef,
+                const Table &table, size_t align, size_t size) {
+  fbb.Align(align);
+  fbb.PushBytes(table.GetStruct<const uint8_t *>(fielddef.offset()), size);
+  fbb.TrackField(fielddef.offset(), fbb.GetSize());
+}
+
+static bool VerifyStruct(flatbuffers::Verifier &v,
+                  const flatbuffers::Table &parent_table,
+                  voffset_t field_offset, const reflection::Object &obj,
+                  bool required) {
+  auto offset = parent_table.GetOptionalFieldOffset(field_offset);
+  if (required && !offset) { return false; }
+
+  return !offset ||
+         v.VerifyFieldStruct(reinterpret_cast<const uint8_t *>(&parent_table),
+                             offset, obj.bytesize(), obj.minalign());
+}
+
+static bool VerifyVectorOfStructs(flatbuffers::Verifier &v,
+                           const flatbuffers::Table &parent_table,
+                           voffset_t field_offset,
+                           const reflection::Object &obj, bool required) {
+  auto p = parent_table.GetPointer<const uint8_t *>(field_offset);
+  if (required && !p) { return false; }
+
+  return !p || v.VerifyVectorOrString(p, obj.bytesize());
+}
+
+// forward declare to resolve cyclic deps between VerifyObject and VerifyVector
+static bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
+                  const reflection::Object &obj,
+                  const flatbuffers::Table *table, bool required);
+
+static bool VerifyUnion(flatbuffers::Verifier &v, const reflection::Schema &schema,
+                 uint8_t utype, const uint8_t *elem,
+                 const reflection::Field &union_field) {
+  if (!utype) return true;  // Not present.
+  auto fb_enum = schema.enums()->Get(union_field.type()->index());
+  if (utype >= fb_enum->values()->size()) return false;
+  auto elem_type = fb_enum->values()->Get(utype)->union_type();
+  switch (elem_type->base_type()) {
+    case reflection::Obj: {
+      auto elem_obj = schema.objects()->Get(elem_type->index());
+      if (elem_obj->is_struct()) {
+        return v.VerifyFromPointer(elem, elem_obj->bytesize());
+      } else {
+        return VerifyObject(v, schema, *elem_obj,
+                            reinterpret_cast<const flatbuffers::Table *>(elem),
+                            true);
+      }
+    }
+    case reflection::String:
+      return v.VerifyString(
+          reinterpret_cast<const flatbuffers::String *>(elem));
+    default: return false;
+  }
+}
+
+static bool VerifyVector(flatbuffers::Verifier &v, const reflection::Schema &schema,
+                  const flatbuffers::Table &table,
+                  const reflection::Field &vec_field) {
+  FLATBUFFERS_ASSERT(vec_field.type()->base_type() == reflection::Vector);
+  if (!table.VerifyField<uoffset_t>(v, vec_field.offset(), sizeof(uoffset_t)))
+    return false;
+
+  switch (vec_field.type()->element()) {
+    case reflection::UType:
+      return v.VerifyVector(flatbuffers::GetFieldV<uint8_t>(table, vec_field));
+    case reflection::Bool:
+    case reflection::Byte:
+    case reflection::UByte:
+      return v.VerifyVector(flatbuffers::GetFieldV<int8_t>(table, vec_field));
+    case reflection::Short:
+    case reflection::UShort:
+      return v.VerifyVector(flatbuffers::GetFieldV<int16_t>(table, vec_field));
+    case reflection::Int:
+    case reflection::UInt:
+      return v.VerifyVector(flatbuffers::GetFieldV<int32_t>(table, vec_field));
+    case reflection::Long:
+    case reflection::ULong:
+      return v.VerifyVector(flatbuffers::GetFieldV<int64_t>(table, vec_field));
+    case reflection::Float:
+      return v.VerifyVector(flatbuffers::GetFieldV<float>(table, vec_field));
+    case reflection::Double:
+      return v.VerifyVector(flatbuffers::GetFieldV<double>(table, vec_field));
+    case reflection::String: {
+      auto vec_string =
+          flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
+              table, vec_field);
+      if (v.VerifyVector(vec_string) && v.VerifyVectorOfStrings(vec_string)) {
+        return true;
+      } else {
+        return false;
+      }
+    }
+    case reflection::Obj: {
+      auto obj = schema.objects()->Get(vec_field.type()->index());
+      if (obj->is_struct()) {
+        return VerifyVectorOfStructs(v, table, vec_field.offset(), *obj,
+                                     vec_field.required());
+      } else {
+        auto vec =
+            flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::Table>>(
+                table, vec_field);
+        if (!v.VerifyVector(vec)) return false;
+        if (!vec) return true;
+        for (uoffset_t j = 0; j < vec->size(); j++) {
+          if (!VerifyObject(v, schema, *obj, vec->Get(j), true)) {
+            return false;
+          }
+        }
+        return true;
+      }
+    }
+    case reflection::Union: {
+      auto vec = flatbuffers::GetFieldV<flatbuffers::Offset<uint8_t>>(
+          table, vec_field);
+      if (!v.VerifyVector(vec)) return false;
+      if (!vec) return true;
+      auto type_vec = table.GetPointer<Vector<uint8_t> *>(vec_field.offset() -
+                                                          sizeof(voffset_t));
+      if (!v.VerifyVector(type_vec)) return false;
+      for (uoffset_t j = 0; j < vec->size(); j++) {
+        //  get union type from the prev field
+        auto utype = type_vec->Get(j);
+        auto elem = vec->Get(j);
+        if (!VerifyUnion(v, schema, utype, elem, vec_field)) return false;
+      }
+      return true;
+    }
+    case reflection::Vector:
+    case reflection::None:
+    default: FLATBUFFERS_ASSERT(false); return false;
+  }
+}
+
+static bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
+                  const reflection::Object &obj,
+                  const flatbuffers::Table *table, bool required) {
+  if (!table) return !required;
+  if (!table->VerifyTableStart(v)) return false;
+  for (uoffset_t i = 0; i < obj.fields()->size(); i++) {
+    auto field_def = obj.fields()->Get(i);
+    switch (field_def->type()->base_type()) {
+      case reflection::None: FLATBUFFERS_ASSERT(false); break;
+      case reflection::UType:
+        if (!table->VerifyField<uint8_t>(v, field_def->offset(),
+                                         sizeof(uint8_t)))
+          return false;
+        break;
+      case reflection::Bool:
+      case reflection::Byte:
+      case reflection::UByte:
+        if (!table->VerifyField<int8_t>(v, field_def->offset(), sizeof(int8_t)))
+          return false;
+        break;
+      case reflection::Short:
+      case reflection::UShort:
+        if (!table->VerifyField<int16_t>(v, field_def->offset(),
+                                         sizeof(int16_t)))
+          return false;
+        break;
+      case reflection::Int:
+      case reflection::UInt:
+        if (!table->VerifyField<int32_t>(v, field_def->offset(),
+                                         sizeof(int32_t)))
+          return false;
+        break;
+      case reflection::Long:
+      case reflection::ULong:
+        if (!table->VerifyField<int64_t>(v, field_def->offset(),
+                                         sizeof(int64_t)))
+          return false;
+        break;
+      case reflection::Float:
+        if (!table->VerifyField<float>(v, field_def->offset(), sizeof(float)))
+          return false;
+        break;
+      case reflection::Double:
+        if (!table->VerifyField<double>(v, field_def->offset(), sizeof(double)))
+          return false;
+        break;
+      case reflection::String:
+        if (!table->VerifyField<uoffset_t>(v, field_def->offset(),
+                                           sizeof(uoffset_t)) ||
+            !v.VerifyString(flatbuffers::GetFieldS(*table, *field_def))) {
+          return false;
+        }
+        break;
+      case reflection::Vector:
+        if (!VerifyVector(v, schema, *table, *field_def)) return false;
+        break;
+      case reflection::Obj: {
+        auto child_obj = schema.objects()->Get(field_def->type()->index());
+        if (child_obj->is_struct()) {
+          if (!VerifyStruct(v, *table, field_def->offset(), *child_obj,
+                            field_def->required())) {
+            return false;
+          }
+        } else {
+          if (!VerifyObject(v, schema, *child_obj,
+                            flatbuffers::GetFieldT(*table, *field_def),
+                            field_def->required())) {
+            return false;
+          }
+        }
+        break;
+      }
+      case reflection::Union: {
+        //  get union type from the prev field
+        voffset_t utype_offset = field_def->offset() - sizeof(voffset_t);
+        auto utype = table->GetField<uint8_t>(utype_offset, 0);
+        auto uval = reinterpret_cast<const uint8_t *>(
+            flatbuffers::GetFieldT(*table, *field_def));
+        if (!VerifyUnion(v, schema, utype, uval, *field_def)) { return false; }
+        break;
+      }
+      default: FLATBUFFERS_ASSERT(false); break;
+    }
+  }
+
+  if (!v.EndTable()) return false;
+
+  return true;
+}
+
+
+} // namespace
+
 int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) {
   // clang-format off
   #define FLATBUFFERS_GET(T) static_cast<int64_t>(ReadScalar<T>(data))
@@ -120,6 +352,23 @@
   }
 }
 
+void ForAllFields(const reflection::Object *object, bool reverse,
+                  std::function<void(const reflection::Field *)> func) {
+  std::vector<uint32_t> field_to_id_map;
+  field_to_id_map.resize(object->fields()->size());
+
+  // Create the mapping of field ID to the index into the vector.
+  for (uint32_t i = 0; i < object->fields()->size(); ++i) {
+    auto field = object->fields()->Get(i);
+    field_to_id_map[field->id()] = i;
+  }
+
+  for (size_t i = 0; i < field_to_id_map.size(); ++i) {
+    func(object->fields()->Get(
+        field_to_id_map[reverse ? field_to_id_map.size() - i + 1 : i]));
+  }
+}
+
 void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) {
   // clang-format off
   #define FLATBUFFERS_SET(T) WriteScalar(data, static_cast<T>(val))
@@ -367,12 +616,8 @@
   return flatbuf.data() + insertion_point + root_offset;
 }
 
-void CopyInline(FlatBufferBuilder &fbb, const reflection::Field &fielddef,
-                const Table &table, size_t align, size_t size) {
-  fbb.Align(align);
-  fbb.PushBytes(table.GetStruct<const uint8_t *>(fielddef.offset()), size);
-  fbb.TrackField(fielddef.offset(), fbb.GetSize());
-}
+
+
 
 Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
                                 const reflection::Schema &schema,
@@ -498,231 +743,22 @@
   }
 }
 
-bool VerifyStruct(flatbuffers::Verifier &v,
-                  const flatbuffers::Table &parent_table,
-                  voffset_t field_offset, const reflection::Object &obj,
-                  bool required) {
-  auto offset = parent_table.GetOptionalFieldOffset(field_offset);
-  if (required && !offset) { return false; }
-
-  return !offset ||
-         v.VerifyFieldStruct(reinterpret_cast<const uint8_t *>(&parent_table),
-                             offset, obj.bytesize(), obj.minalign());
-}
-
-bool VerifyVectorOfStructs(flatbuffers::Verifier &v,
-                           const flatbuffers::Table &parent_table,
-                           voffset_t field_offset,
-                           const reflection::Object &obj, bool required) {
-  auto p = parent_table.GetPointer<const uint8_t *>(field_offset);
-  if (required && !p) { return false; }
-
-  return !p || v.VerifyVectorOrString(p, obj.bytesize());
-}
-
-// forward declare to resolve cyclic deps between VerifyObject and VerifyVector
-bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
-                  const reflection::Object &obj,
-                  const flatbuffers::Table *table, bool required);
-
-bool VerifyUnion(flatbuffers::Verifier &v, const reflection::Schema &schema,
-                 uint8_t utype, const uint8_t *elem,
-                 const reflection::Field &union_field) {
-  if (!utype) return true;  // Not present.
-  auto fb_enum = schema.enums()->Get(union_field.type()->index());
-  if (utype >= fb_enum->values()->size()) return false;
-  auto elem_type = fb_enum->values()->Get(utype)->union_type();
-  switch (elem_type->base_type()) {
-    case reflection::Obj: {
-      auto elem_obj = schema.objects()->Get(elem_type->index());
-      if (elem_obj->is_struct()) {
-        return v.VerifyFromPointer(elem, elem_obj->bytesize());
-      } else {
-        return VerifyObject(v, schema, *elem_obj,
-                            reinterpret_cast<const flatbuffers::Table *>(elem),
-                            true);
-      }
-    }
-    case reflection::String:
-      return v.VerifyString(
-          reinterpret_cast<const flatbuffers::String *>(elem));
-    default: return false;
-  }
-}
-
-bool VerifyVector(flatbuffers::Verifier &v, const reflection::Schema &schema,
-                  const flatbuffers::Table &table,
-                  const reflection::Field &vec_field) {
-  FLATBUFFERS_ASSERT(vec_field.type()->base_type() == reflection::Vector);
-  if (!table.VerifyField<uoffset_t>(v, vec_field.offset(), sizeof(uoffset_t)))
-    return false;
-
-  switch (vec_field.type()->element()) {
-    case reflection::UType:
-      return v.VerifyVector(flatbuffers::GetFieldV<uint8_t>(table, vec_field));
-    case reflection::Bool:
-    case reflection::Byte:
-    case reflection::UByte:
-      return v.VerifyVector(flatbuffers::GetFieldV<int8_t>(table, vec_field));
-    case reflection::Short:
-    case reflection::UShort:
-      return v.VerifyVector(flatbuffers::GetFieldV<int16_t>(table, vec_field));
-    case reflection::Int:
-    case reflection::UInt:
-      return v.VerifyVector(flatbuffers::GetFieldV<int32_t>(table, vec_field));
-    case reflection::Long:
-    case reflection::ULong:
-      return v.VerifyVector(flatbuffers::GetFieldV<int64_t>(table, vec_field));
-    case reflection::Float:
-      return v.VerifyVector(flatbuffers::GetFieldV<float>(table, vec_field));
-    case reflection::Double:
-      return v.VerifyVector(flatbuffers::GetFieldV<double>(table, vec_field));
-    case reflection::String: {
-      auto vec_string =
-          flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
-              table, vec_field);
-      if (v.VerifyVector(vec_string) && v.VerifyVectorOfStrings(vec_string)) {
-        return true;
-      } else {
-        return false;
-      }
-    }
-    case reflection::Obj: {
-      auto obj = schema.objects()->Get(vec_field.type()->index());
-      if (obj->is_struct()) {
-        return VerifyVectorOfStructs(v, table, vec_field.offset(), *obj,
-                                     vec_field.required());
-      } else {
-        auto vec =
-            flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::Table>>(
-                table, vec_field);
-        if (!v.VerifyVector(vec)) return false;
-        if (!vec) return true;
-        for (uoffset_t j = 0; j < vec->size(); j++) {
-          if (!VerifyObject(v, schema, *obj, vec->Get(j), true)) {
-            return false;
-          }
-        }
-        return true;
-      }
-    }
-    case reflection::Union: {
-      auto vec = flatbuffers::GetFieldV<flatbuffers::Offset<uint8_t>>(
-          table, vec_field);
-      if (!v.VerifyVector(vec)) return false;
-      if (!vec) return true;
-      auto type_vec = table.GetPointer<Vector<uint8_t> *>(vec_field.offset() -
-                                                          sizeof(voffset_t));
-      if (!v.VerifyVector(type_vec)) return false;
-      for (uoffset_t j = 0; j < vec->size(); j++) {
-        //  get union type from the prev field
-        auto utype = type_vec->Get(j);
-        auto elem = vec->Get(j);
-        if (!VerifyUnion(v, schema, utype, elem, vec_field)) return false;
-      }
-      return true;
-    }
-    case reflection::Vector:
-    case reflection::None:
-    default: FLATBUFFERS_ASSERT(false); return false;
-  }
-}
-
-bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
-                  const reflection::Object &obj,
-                  const flatbuffers::Table *table, bool required) {
-  if (!table) return !required;
-  if (!table->VerifyTableStart(v)) return false;
-  for (uoffset_t i = 0; i < obj.fields()->size(); i++) {
-    auto field_def = obj.fields()->Get(i);
-    switch (field_def->type()->base_type()) {
-      case reflection::None: FLATBUFFERS_ASSERT(false); break;
-      case reflection::UType:
-        if (!table->VerifyField<uint8_t>(v, field_def->offset(),
-                                         sizeof(uint8_t)))
-          return false;
-        break;
-      case reflection::Bool:
-      case reflection::Byte:
-      case reflection::UByte:
-        if (!table->VerifyField<int8_t>(v, field_def->offset(), sizeof(int8_t)))
-          return false;
-        break;
-      case reflection::Short:
-      case reflection::UShort:
-        if (!table->VerifyField<int16_t>(v, field_def->offset(),
-                                         sizeof(int16_t)))
-          return false;
-        break;
-      case reflection::Int:
-      case reflection::UInt:
-        if (!table->VerifyField<int32_t>(v, field_def->offset(),
-                                         sizeof(int32_t)))
-          return false;
-        break;
-      case reflection::Long:
-      case reflection::ULong:
-        if (!table->VerifyField<int64_t>(v, field_def->offset(),
-                                         sizeof(int64_t)))
-          return false;
-        break;
-      case reflection::Float:
-        if (!table->VerifyField<float>(v, field_def->offset(), sizeof(float)))
-          return false;
-        break;
-      case reflection::Double:
-        if (!table->VerifyField<double>(v, field_def->offset(), sizeof(double)))
-          return false;
-        break;
-      case reflection::String:
-        if (!table->VerifyField<uoffset_t>(v, field_def->offset(),
-                                           sizeof(uoffset_t)) ||
-            !v.VerifyString(flatbuffers::GetFieldS(*table, *field_def))) {
-          return false;
-        }
-        break;
-      case reflection::Vector:
-        if (!VerifyVector(v, schema, *table, *field_def)) return false;
-        break;
-      case reflection::Obj: {
-        auto child_obj = schema.objects()->Get(field_def->type()->index());
-        if (child_obj->is_struct()) {
-          if (!VerifyStruct(v, *table, field_def->offset(), *child_obj,
-                            field_def->required())) {
-            return false;
-          }
-        } else {
-          if (!VerifyObject(v, schema, *child_obj,
-                            flatbuffers::GetFieldT(*table, *field_def),
-                            field_def->required())) {
-            return false;
-          }
-        }
-        break;
-      }
-      case reflection::Union: {
-        //  get union type from the prev field
-        voffset_t utype_offset = field_def->offset() - sizeof(voffset_t);
-        auto utype = table->GetField<uint8_t>(utype_offset, 0);
-        auto uval = reinterpret_cast<const uint8_t *>(
-            flatbuffers::GetFieldT(*table, *field_def));
-        if (!VerifyUnion(v, schema, utype, uval, *field_def)) { return false; }
-        break;
-      }
-      default: FLATBUFFERS_ASSERT(false); break;
-    }
-  }
-
-  if (!v.EndTable()) return false;
-
-  return true;
-}
 
 bool Verify(const reflection::Schema &schema, const reflection::Object &root,
-            const uint8_t *buf, size_t length, uoffset_t max_depth /*= 64*/,
-            uoffset_t max_tables /*= 1000000*/) {
+            const uint8_t *const buf, const size_t length,
+            const uoffset_t max_depth, const uoffset_t max_tables) {
   Verifier v(buf, length, max_depth, max_tables);
-  return VerifyObject(v, schema, root, flatbuffers::GetAnyRoot(buf), true);
+  return VerifyObject(v, schema, root, flatbuffers::GetAnyRoot(buf),
+                      /*required=*/true);
+}
+
+bool VerifySizePrefixed(const reflection::Schema &schema,
+                        const reflection::Object &root,
+                        const uint8_t *const buf, const size_t length,
+                        const uoffset_t max_depth, const uoffset_t max_tables) {
+  Verifier v(buf, length, max_depth, max_tables);
+  return VerifyObject(v, schema, root, flatbuffers::GetAnySizePrefixedRoot(buf),
+                      /*required=*/true);
 }
 
 }  // namespace flatbuffers
diff --git a/src/util.cpp b/src/util.cpp
index a1ed6c1..df324e8 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -17,13 +17,14 @@
 // clang-format off
 // Dont't remove `format off`, it prevent reordering of win-includes.
 
+#include <cstring>
 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) || \
     defined(__QNXNTO__)
 #  define _POSIX_C_SOURCE 200809L
 #  define _XOPEN_SOURCE 700L
 #endif
 
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__)
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
@@ -34,7 +35,9 @@
 #    include <crtdbg.h>
 #  endif
 #  include <windows.h>  // Must be included before <direct.h>
-#  include <direct.h>
+#  ifndef __CYGWIN__
+#    include <direct.h>
+#  endif
 #  include <winbase.h>
 #  undef interface  // This is also important because of reasons
 #endif
@@ -46,19 +49,21 @@
 
 #include <clocale>
 #include <cstdlib>
-#include <functional>
 #include <fstream>
+#include <functional>
 
 #include "flatbuffers/base.h"
 
 namespace flatbuffers {
 
-bool FileExistsRaw(const char *name) {
+namespace {
+
+static bool FileExistsRaw(const char *name) {
   std::ifstream ifs(name);
   return ifs.good();
 }
 
-bool LoadFileRaw(const char *name, bool binary, std::string *buf) {
+static bool LoadFileRaw(const char *name, bool binary, std::string *buf) {
   if (DirExists(name)) return false;
   std::ifstream ifs(name, binary ? std::ifstream::binary : std::ifstream::in);
   if (!ifs.is_open()) return false;
@@ -78,8 +83,124 @@
   return !ifs.bad();
 }
 
-static LoadFileFunction g_load_file_function = LoadFileRaw;
-static FileExistsFunction g_file_exists_function = FileExistsRaw;
+LoadFileFunction g_load_file_function = LoadFileRaw;
+FileExistsFunction g_file_exists_function = FileExistsRaw;
+
+static std::string ToCamelCase(const std::string &input, bool first) {
+  std::string s;
+  for (size_t i = 0; i < input.length(); i++) {
+    if (!i && first)
+      s += CharToUpper(input[i]);
+    else if (input[i] == '_' && i + 1 < input.length())
+      s += CharToUpper(input[++i]);
+    else
+      s += input[i];
+  }
+  return s;
+}
+
+static std::string ToSnakeCase(const std::string &input, bool screaming) {
+  std::string s;
+  for (size_t i = 0; i < input.length(); i++) {
+    if (i == 0) {
+      s += screaming ? CharToUpper(input[i]) : CharToLower(input[i]);
+    } else if (input[i] == '_') {
+      s += '_';
+    } else if (!islower(input[i])) {
+      // Prevent duplicate underscores for Upper_Snake_Case strings
+      // and UPPERCASE strings.
+      if (islower(input[i - 1])) { s += '_'; }
+      s += screaming ? CharToUpper(input[i]) : CharToLower(input[i]);
+    } else {
+      s += screaming ? CharToUpper(input[i]) : input[i];
+    }
+  }
+  return s;
+}
+
+std::string ToAll(const std::string &input,
+                         std::function<char(const char)> transform) {
+  std::string s;
+  for (size_t i = 0; i < input.length(); i++) { s += transform(input[i]); }
+  return s;
+}
+
+std::string CamelToSnake(const std::string &input) {
+  std::string s;
+  for (size_t i = 0; i < input.length(); i++) {
+    if (i == 0) {
+      s += CharToLower(input[i]);
+    } else if (input[i] == '_') {
+      s += '_';
+    } else if (!islower(input[i])) {
+      // Prevent duplicate underscores for Upper_Snake_Case strings
+      // and UPPERCASE strings.
+      if (islower(input[i - 1])) { s += '_'; }
+      s += CharToLower(input[i]);
+    } else {
+      s += input[i];
+    }
+  }
+  return s;
+}
+
+std::string DasherToSnake(const std::string &input) {
+  std::string s;
+  for (size_t i = 0; i < input.length(); i++) {
+    if (input[i] == '-') {
+      s += "_";
+    } else {
+      s += input[i];
+    }
+  }
+  return s;
+}
+
+std::string ToDasher(const std::string &input) {
+  std::string s;
+  char p = 0;
+  for (size_t i = 0; i < input.length(); i++) {
+    char const &c = input[i];
+    if (c == '_') {
+      if (i > 0 && p != kPathSeparator &&
+          // The following is a special case to ignore digits after a _. This is
+          // because ThisExample3 would be converted to this_example_3 in the
+          // CamelToSnake conversion, and then dasher would do this-example-3,
+          // but it expects this-example3.
+          !(i + 1 < input.length() && isdigit(input[i + 1])))
+        s += "-";
+    } else {
+      s += c;
+    }
+    p = c;
+  }
+  return s;
+}
+
+
+// Converts foo_bar_123baz_456 to foo_bar123_baz456
+std::string SnakeToSnake2(const std::string &s) {
+  if (s.length() <= 1) return s;
+  std::string result;
+  result.reserve(s.size());
+  for (size_t i = 0; i < s.length() - 1; i++) {
+    if (s[i] == '_' && isdigit(s[i + 1])) {
+      continue;  // Move the `_` until after the digits.
+    }
+
+    result.push_back(s[i]);
+
+    if (isdigit(s[i]) && isalpha(s[i + 1]) && islower(s[i + 1])) {
+      result.push_back('_');
+    }
+  }
+  result.push_back(s.back());
+
+  return result;
+}
+
+} // namespace
+
 
 bool LoadFile(const char *name, bool binary, std::string *buf) {
   FLATBUFFERS_ASSERT(g_load_file_function);
@@ -155,6 +276,15 @@
   return i != std::string::npos ? filepath.substr(0, i) : "";
 }
 
+std::string StripPrefix(const std::string &filepath,
+                        const std::string &prefix_to_remove) {
+  if (!strncmp(filepath.c_str(), prefix_to_remove.c_str(),
+               prefix_to_remove.size())) {
+    return filepath.substr(prefix_to_remove.size());
+  }
+  return filepath;
+}
+
 std::string ConCatPathFileName(const std::string &path,
                                const std::string &filename) {
   std::string filepath = path;
@@ -202,7 +332,7 @@
   #ifdef FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION
     return filepath;
   #else
-    #ifdef _WIN32
+    #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__)
       char abs_path[MAX_PATH];
       return GetFullPathNameA(filepath.c_str(), MAX_PATH, abs_path, nullptr)
     #else
@@ -296,124 +426,6 @@
   return true;
 }
 
-void SetupDefaultCRTReportMode() {
-  // clang-format off
-
-  #ifdef _MSC_VER
-    // By default, send all reports to STDOUT to prevent CI hangs.
-    // Enable assert report box [Abort|Retry|Ignore] if a debugger is present.
-    const int dbg_mode = (_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG) |
-                         (IsDebuggerPresent() ? _CRTDBG_MODE_WNDW : 0);
-    (void)dbg_mode; // release mode fix
-    // CrtDebug reports to _CRT_WARN channel.
-    _CrtSetReportMode(_CRT_WARN, dbg_mode);
-    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
-    // The assert from <assert.h> reports to _CRT_ERROR channel
-    _CrtSetReportMode(_CRT_ERROR, dbg_mode);
-    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
-    // Internal CRT assert channel?
-    _CrtSetReportMode(_CRT_ASSERT, dbg_mode);
-    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
-  #endif
-
-  // clang-format on
-}
-
-namespace {
-
-static std::string ToCamelCase(const std::string &input, bool first) {
-  std::string s;
-  for (size_t i = 0; i < input.length(); i++) {
-    if (!i && first)
-      s += CharToUpper(input[i]);
-    else if (input[i] == '_' && i + 1 < input.length())
-      s += CharToUpper(input[++i]);
-    else
-      s += input[i];
-  }
-  return s;
-}
-
-static std::string ToSnakeCase(const std::string &input, bool screaming) {
-  std::string s;
-  for (size_t i = 0; i < input.length(); i++) {
-    if (i == 0) {
-      s += screaming ? CharToUpper(input[i]) : CharToLower(input[i]);
-    } else if (input[i] == '_') {
-      s += '_';
-    } else if (!islower(input[i])) {
-      // Prevent duplicate underscores for Upper_Snake_Case strings
-      // and UPPERCASE strings.
-      if (islower(input[i - 1])) { s += '_'; }
-      s += screaming ? CharToUpper(input[i]) : CharToLower(input[i]);
-    } else {
-      s += screaming ? CharToUpper(input[i]) : input[i];
-    }
-  }
-  return s;
-}
-
-static std::string ToAll(const std::string &input,
-                         std::function<char(const char)> transform) {
-  std::string s;
-  for (size_t i = 0; i < input.length(); i++) { s += transform(input[i]); }
-  return s;
-}
-
-static std::string CamelToSnake(const std::string &input) {
-  std::string s;
-  for (size_t i = 0; i < input.length(); i++) {
-    if (i == 0) {
-      s += CharToLower(input[i]);
-    } else if (input[i] == '_') {
-      s += '_';
-    } else if (!islower(input[i])) {
-      // Prevent duplicate underscores for Upper_Snake_Case strings
-      // and UPPERCASE strings.
-      if (islower(input[i - 1])) { s += '_'; }
-      s += CharToLower(input[i]);
-    } else {
-      s += input[i];
-    }
-  }
-  return s;
-}
-
-static std::string DasherToSnake(const std::string &input) {
-  std::string s;
-  for (size_t i = 0; i < input.length(); i++) {
-    if (input[i] == '-') {
-      s += "_";
-    } else {
-      s += input[i];
-    }
-  }
-  return s;
-}
-
-static std::string ToDasher(const std::string &input) {
-  std::string s;
-  char p = 0;
-  for (size_t i = 0; i < input.length(); i++) {
-    char const &c = input[i];
-    if (c == '_') {
-      if (i > 0 && p != kPathSeparator &&
-          // The following is a special case to ignore digits after a _. This is
-          // because ThisExample3 would be converted to this_example_3 in the
-          // CamelToSnake conversion, and then dasher would do this-example-3,
-          // but it expects this-example3.
-          !(i + 1 < input.length() && isdigit(input[i + 1])))
-        s += "-";
-    } else {
-      s += c;
-    }
-    p = c;
-  }
-  return s;
-}
-
-}  // namespace
-
 std::string ConvertCase(const std::string &input, Case output_case,
                         Case input_case) {
   if (output_case == Case::kKeep) return input;
@@ -440,6 +452,7 @@
     case Case::kAllUpper: return ToAll(input, CharToUpper);
     case Case::kAllLower: return ToAll(input, CharToLower);
     case Case::kDasher: return ToDasher(input);
+    case Case::kSnake2: return SnakeToSnake2(input);
     default:
     case Case::kUnknown: return input;
   }