Addressed Tavis's comments in http://codereview.chromium.org/3023041/. 

- Modified 4-bytes 16.16 Fixed-point number decoder in ReadNextNumberFromType2CharString(). 
- Fixed off-by-one error in ExecuteType2CharStringOperator(). 
- Fixed checks of 'div' and 'endchar' operators. They were not strict enough. 

- Wrote ~1600 lines of test to verify if the modification above is valid. 
- Moved Type2CharStringOperator enum definition from cff_type2_charstriung.cc tocff_type2_charstriung.h to write the unit tests. 

Review: http://codereview.chromium.org/3027049

BUG=51070
TEST=http://code.google.com/p/ots/wiki/HowToTestOts (Verified that the modified OTS rejects all malformed fonts, and does not reject all non-malformed fonts. I tested ~3400 TrueType and ~450 OpenType fonts.) 
TEST=ran cff_type2_charstring_test. 



git-svn-id: http://ots.googlecode.com/svn/trunk@35 a4e77c2c-9104-11de-800e-5b313e0d2bf3
diff --git a/src/cff_type2_charstring.cc b/src/cff_type2_charstring.cc
index 6a0d57f..2137c62 100644
--- a/src/cff_type2_charstring.cc
+++ b/src/cff_type2_charstring.cc
@@ -7,73 +7,27 @@
 
 #include "cff_type2_charstring.h"
 
+#include <climits>
 #include <cstdio>
 #include <cstring>
-#include <limits>
 #include <stack>
 #include <string>
 #include <utility>
 
 namespace {
 
-// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
-enum Type2CharStringOperator {
-  kHStem = 1,
-  kVStem = 3,
-  kVMoveTo = 4,
-  kRLineTo = 5,
-  kHLineTo = 6,
-  kVLineTo = 7,
-  kRRCurveTo = 8,
-  kCallSubr = 10,
-  kReturn = 11,
-  kEndChar = 14,
-  kHStemHm = 18,
-  kHintMask = 19,
-  kCntrMask = 20,
-  kRMoveTo = 21,
-  kHMoveTo = 22,
-  kVStemHm = 23,
-  kRCurveLine = 24,
-  kRLineCurve = 25,
-  kVVCurveTo = 26,
-  kHHCurveTo = 27,
-  kCallGSubr = 29,
-  kVHCurveTo = 30,
-  kHVCurveTo = 31,
-  kAnd = (12 << 8) + 3,
-  kOr = (12 << 8) + 4,
-  kNot = (12 << 8) + 5,
-  kAbs = (12 << 8) + 9,
-  kAdd = (12 << 8) + 10,
-  kSub = (12 << 8) + 11,
-  kDiv = (12 << 8) + 12,
-  kNeg = (12 << 8) + 14,
-  kEq = (12 << 8) + 15,
-  kDrop = (12 << 8) + 18,
-  kPut = (12 << 8) + 20,
-  kGet = (12 << 8) + 21,
-  kIfElse = (12 << 8) + 22,
-  kRandom = (12 << 8) + 23,
-  kMul = (12 << 8) + 24,
-  kSqrt = (12 << 8) + 26,
-  kDup = (12 << 8) + 27,
-  kExch = (12 << 8) + 28,
-  kIndex = (12 << 8) + 29,
-  kRoll = (12 << 8) + 30,
-  kHFlex = (12 << 8) + 34,
-  kFlex = (12 << 8) + 35,
-  kHFlex1 = (12 << 8) + 36,
-  kFlex1 = (12 << 8) + 37,
-};
-
 // Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical
 // Note #5177.
+const int32_t kMaxSubrsCount = 65536;
 const size_t kMaxCharStringLength = 65535;
 const size_t kMaxArgumentStack = 48;
 const size_t kMaxNumberOfStemHints = 96;
 const size_t kMaxSubrNesting = 10;
 
+// |dummy_result| should be a huge positive integer so callsubr and callgsubr
+// will fail with the dummy value.
+const int32_t dummy_result = INT_MAX;
+
 bool ExecuteType2CharString(size_t call_depth,
                             const ots::CFFIndex& global_subrs_index,
                             const ots::CFFIndex& local_subrs_index,
@@ -85,101 +39,101 @@
                             size_t *in_out_num_stems);
 
 // Converts |op| to a string and returns it.
-const char *Type2CharStringOperatorToString(Type2CharStringOperator op) {
+const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
   switch (op) {
-  case kHStem:
+  case ots::kHStem:
     return "HStem";
-  case kVStem:
+  case ots::kVStem:
     return "VStem";
-  case kVMoveTo:
+  case ots::kVMoveTo:
     return "VMoveTo";
-  case kRLineTo:
+  case ots::kRLineTo:
     return "RLineTo";
-  case kHLineTo:
+  case ots::kHLineTo:
     return "HLineTo";
-  case kVLineTo:
+  case ots::kVLineTo:
     return "VLineTo";
-  case kRRCurveTo:
+  case ots::kRRCurveTo:
     return "RRCurveTo";
-  case kCallSubr:
+  case ots::kCallSubr:
     return "CallSubr";
-  case kReturn:
+  case ots::kReturn:
     return "Return";
-  case kEndChar:
+  case ots::kEndChar:
     return "EndChar";
-  case kHStemHm:
+  case ots::kHStemHm:
     return "HStemHm";
-  case kHintMask:
+  case ots::kHintMask:
     return "HintMask";
-  case kCntrMask:
+  case ots::kCntrMask:
     return "CntrMask";
-  case kRMoveTo:
+  case ots::kRMoveTo:
     return "RMoveTo";
-  case kHMoveTo:
+  case ots::kHMoveTo:
     return "HMoveTo";
-  case kVStemHm:
+  case ots::kVStemHm:
     return "VStemHm";
-  case kRCurveLine:
+  case ots::kRCurveLine:
     return "RCurveLine";
-  case kRLineCurve:
+  case ots::kRLineCurve:
     return "RLineCurve";
-  case kVVCurveTo:
+  case ots::kVVCurveTo:
     return "VVCurveTo";
-  case kHHCurveTo:
+  case ots::kHHCurveTo:
     return "HHCurveTo";
-  case kCallGSubr:
+  case ots::kCallGSubr:
     return "CallGSubr";
-  case kVHCurveTo:
+  case ots::kVHCurveTo:
     return "VHCurveTo";
-  case kHVCurveTo:
+  case ots::kHVCurveTo:
     return "HVCurveTo";
-  case kAnd:
+  case ots::kAnd:
     return "And";
-  case kOr:
+  case ots::kOr:
     return "Or";
-  case kNot:
+  case ots::kNot:
     return "Not";
-  case kAbs:
+  case ots::kAbs:
     return "Abs";
-  case kAdd:
+  case ots::kAdd:
     return "Add";
-  case kSub:
+  case ots::kSub:
     return "Sub";
-  case kDiv:
+  case ots::kDiv:
     return "Div";
-  case kNeg:
+  case ots::kNeg:
     return "Neg";
-  case kEq:
+  case ots::kEq:
     return "Eq";
-  case kDrop:
+  case ots::kDrop:
     return "Drop";
-  case kPut:
+  case ots::kPut:
     return "Put";
-  case kGet:
+  case ots::kGet:
     return "Get";
-  case kIfElse:
+  case ots::kIfElse:
     return "IfElse";
-  case kRandom:
+  case ots::kRandom:
     return "Random";
-  case kMul:
+  case ots::kMul:
     return "Mul";
-  case kSqrt:
+  case ots::kSqrt:
     return "Sqrt";
-  case kDup:
+  case ots::kDup:
     return "Dup";
-  case kExch:
+  case ots::kExch:
     return "Exch";
-  case kIndex:
+  case ots::kIndex:
     return "Index";
-  case kRoll:
+  case ots::kRoll:
     return "Roll";
-  case kHFlex:
+  case ots::kHFlex:
     return "HFlex";
-  case kFlex:
+  case ots::kFlex:
     return "Flex";
-  case kHFlex1:
+  case ots::kHFlex1:
     return "HFlex1";
-  case kFlex1:
+  case ots::kFlex1:
     return "Flex1";
   }
 
@@ -246,16 +200,13 @@
     *out_number = -((static_cast<int32_t>(v) - 251) * 256) -
         static_cast<int32_t>(w) - 108;
   } else if (v == 255) {
-    uint32_t result = 0;
-    for (size_t i = 0; i < 4; ++i) {
-      if (!char_string->ReadU8(&v)) {
-        return OTS_FAILURE();
-      }
-      result = (result << 8) + v;
-    }
-    // TODO(yusukes): |result| should be treated as 16.16 fixed-point number
+    // TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255,
+    // we should treat the following 4-bytes as a 16.16 fixed-point number
     // rather than 32bit signed int.
-    *out_number = static_cast<int32_t>(result);
+    if (!char_string->Skip(4)) {
+      return OTS_FAILURE();
+    }
+    *out_number = dummy_result;
   } else {
     return OTS_FAILURE();
   }
@@ -277,16 +228,13 @@
                                     bool *out_found_endchar,
                                     bool *in_out_found_width,
                                     size_t *in_out_num_stems) {
-  // |dummy_result| should be a huge positive integer so callsubr and callgsubr
-  // will fail with the dummy value.
-  const int32_t dummy_result = std::numeric_limits<int>::max();
   const size_t stack_size = argument_stack->size();
 
   switch (op) {
-  case kCallSubr:
-  case kCallGSubr: {
+  case ots::kCallSubr:
+  case ots::kCallGSubr: {
     const ots::CFFIndex& subrs_index =
-      (op == kCallSubr ? local_subrs_index : global_subrs_index);
+        (op == ots::kCallSubr ? local_subrs_index : global_subrs_index);
 
     if (stack_size < 1) {
       return OTS_FAILURE();
@@ -314,7 +262,10 @@
     if (subr_number < 0) {
       return OTS_FAILURE();
     }
-    if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number)) {
+    if (subr_number >= kMaxSubrsCount) {
+      return OTS_FAILURE();
+    }
+    if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) {
       return OTS_FAILURE();  // The number is out-of-bounds.
     }
 
@@ -342,18 +293,18 @@
                                   in_out_num_stems);
   }
 
-  case kReturn:
+  case ots::kReturn:
     return true;
 
-  case kEndChar:
+  case ots::kEndChar:
     *out_found_endchar = true;
     *in_out_found_width = true;  // just in case.
     return true;
 
-  case kHStem:
-  case kVStem:
-  case kHStemHm:
-  case kVStemHm: {
+  case ots::kHStem:
+  case ots::kVStem:
+  case ots::kHStemHm:
+  case ots::kVStemHm: {
     bool successful = false;
     if (stack_size < 2) {
       return OTS_FAILURE();
@@ -375,7 +326,7 @@
     return successful ? true : OTS_FAILURE();
   }
 
-  case kRMoveTo: {
+  case ots::kRMoveTo: {
     bool successful = false;
     if (stack_size == 2) {
       successful = true;
@@ -388,8 +339,8 @@
     return successful ? true : OTS_FAILURE();
   }
 
-  case kVMoveTo:
-  case kHMoveTo: {
+  case ots::kVMoveTo:
+  case ots::kHMoveTo: {
     bool successful = false;
     if (stack_size == 1) {
       successful = true;
@@ -402,8 +353,8 @@
     return successful ? true : OTS_FAILURE();
   }
 
-  case kHintMask:
-  case kCntrMask: {
+  case ots::kHintMask:
+  case ots::kCntrMask: {
     bool successful = false;
     if (stack_size == 0) {
       successful = true;
@@ -437,7 +388,7 @@
     return true;
   }
 
-  case kRLineTo:
+  case ots::kRLineTo:
     if (!(*in_out_found_width)) {
       // The first stack-clearing operator should be one of hstem, hstemhm,
       // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or
@@ -454,8 +405,8 @@
       argument_stack->pop();
     return true;
 
-  case kHLineTo:
-  case kVLineTo:
+  case ots::kHLineTo:
+  case ots::kVLineTo:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -466,7 +417,7 @@
       argument_stack->pop();
     return true;
 
-  case kRRCurveTo:
+  case ots::kRRCurveTo:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -480,7 +431,7 @@
       argument_stack->pop();
     return true;
 
-  case kRCurveLine:
+  case ots::kRCurveLine:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -494,7 +445,7 @@
       argument_stack->pop();
     return true;
 
-  case kRLineCurve:
+  case ots::kRLineCurve:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -508,7 +459,7 @@
       argument_stack->pop();
     return true;
 
-  case kVVCurveTo:
+  case ots::kVVCurveTo:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -523,7 +474,7 @@
       argument_stack->pop();
     return true;
 
-  case kHHCurveTo: {
+  case ots::kHHCurveTo: {
     bool successful = false;
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
@@ -543,8 +494,8 @@
     return successful ? true : OTS_FAILURE();
   }
 
-  case kVHCurveTo:
-  case kHVCurveTo: {
+  case ots::kVHCurveTo:
+  case ots::kHVCurveTo: {
     bool successful = false;
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
@@ -573,11 +524,11 @@
     return successful ? true : OTS_FAILURE();
   }
 
-  case kAnd:
-  case kOr:
-  case kEq:
-  case kAdd:
-  case kSub:
+  case ots::kAnd:
+  case ots::kOr:
+  case ots::kEq:
+  case ots::kAdd:
+  case ots::kSub:
     if (stack_size < 2) {
       return OTS_FAILURE();
     }
@@ -588,9 +539,9 @@
     // arithmetic and conditional operations.
     return true;
 
-  case kNot:
-  case kAbs:
-  case kNeg:
+  case ots::kNot:
+  case ots::kAbs:
+  case ots::kNeg:
     if (stack_size < 1) {
       return OTS_FAILURE();
     }
@@ -600,45 +551,46 @@
     // arithmetic and conditional operations.
     return true;
 
-  case kDiv:
+  case ots::kDiv:
     // TODO(yusukes): Should detect div-by-zero errors.
-    if (stack_size < 1) {
+    if (stack_size < 2) {
       return OTS_FAILURE();
     }
     argument_stack->pop();
+    argument_stack->pop();
     argument_stack->push(dummy_result);
     // TODO(yusukes): Implement this. We should push a real value for all
     // arithmetic and conditional operations.
     return true;
 
-  case kDrop:
+  case ots::kDrop:
     if (stack_size < 1) {
       return OTS_FAILURE();
     }
     argument_stack->pop();
     return true;
 
-  case kPut:
-  case kGet:
-  case kIndex:
+  case ots::kPut:
+  case ots::kGet:
+  case ots::kIndex:
     // For now, just call OTS_FAILURE since there is no way to check whether the
     // index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType
     // fonts I have (except malicious ones!) use the operators.
     // TODO(yusukes): Implement them in a secure way.
     return OTS_FAILURE();
 
-  case kRoll:
+  case ots::kRoll:
     // Likewise, just call OTS_FAILURE for kRoll since there is no way to check
     // whether |N| is smaller than the current stack depth or not.
     // TODO(yusukes): Implement them in a secure way.
     return OTS_FAILURE();
 
-  case kRandom:
+  case ots::kRandom:
     // For now, we don't handle the 'random' operator since the operator makes
     // it hard to analyze hinting code statically.
     return OTS_FAILURE();
 
-  case kIfElse:
+  case ots::kIfElse:
     if (stack_size < 4) {
       return OTS_FAILURE();
     }
@@ -651,7 +603,7 @@
     // arithmetic and conditional operations.
     return true;
 
-  case kMul:
+  case ots::kMul:
     // TODO(yusukes): Should detect overflows.
     if (stack_size < 2) {
       return OTS_FAILURE();
@@ -663,7 +615,7 @@
     // arithmetic and conditional operations.
     return true;
 
-  case kSqrt:
+  case ots::kSqrt:
     // TODO(yusukes): Should check if the argument is negative.
     if (stack_size < 1) {
       return OTS_FAILURE();
@@ -674,7 +626,7 @@
     // arithmetic and conditional operations.
     return true;
 
-  case kDup:
+  case ots::kDup:
     if (stack_size < 1) {
       return OTS_FAILURE();
     }
@@ -688,7 +640,7 @@
     // arithmetic and conditional operations.
     return true;
 
-  case kExch:
+  case ots::kExch:
     if (stack_size < 2) {
       return OTS_FAILURE();
     }
@@ -700,7 +652,7 @@
     // arithmetic and conditional operations.
     return true;
 
-  case kHFlex:
+  case ots::kHFlex:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -711,7 +663,7 @@
       argument_stack->pop();
     return true;
 
-  case kFlex:
+  case ots::kFlex:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -722,7 +674,7 @@
       argument_stack->pop();
     return true;
 
-  case kHFlex1:
+  case ots::kHFlex1:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -733,7 +685,7 @@
       argument_stack->pop();
     return true;
 
-  case kFlex1:
+  case ots::kFlex1:
     if (!(*in_out_found_width)) {
       return OTS_FAILURE();
     }
@@ -824,7 +776,7 @@
     if (*out_found_endchar) {
       return true;
     }
-    if (operator_or_operand == kReturn) {
+    if (operator_or_operand == ots::kReturn) {
       return true;
     }
   }
@@ -927,6 +879,9 @@
                                 &found_endchar, &found_width, &num_stems)) {
       return OTS_FAILURE();
     }
+    if (!found_endchar) {
+      return OTS_FAILURE();
+    }
   }
   return true;
 }
diff --git a/src/cff_type2_charstring.h b/src/cff_type2_charstring.h
index 4e6ff6a..dbaea75 100644
--- a/src/cff_type2_charstring.h
+++ b/src/cff_type2_charstring.h
@@ -42,6 +42,59 @@
     const CFFIndex *local_subrs,
     Buffer *cff_table);
 
+// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
+enum Type2CharStringOperator {
+  kHStem = 1,
+  kVStem = 3,
+  kVMoveTo = 4,
+  kRLineTo = 5,
+  kHLineTo = 6,
+  kVLineTo = 7,
+  kRRCurveTo = 8,
+  kCallSubr = 10,
+  kReturn = 11,
+  kEndChar = 14,
+  kHStemHm = 18,
+  kHintMask = 19,
+  kCntrMask = 20,
+  kRMoveTo = 21,
+  kHMoveTo = 22,
+  kVStemHm = 23,
+  kRCurveLine = 24,
+  kRLineCurve = 25,
+  kVVCurveTo = 26,
+  kHHCurveTo = 27,
+  kCallGSubr = 29,
+  kVHCurveTo = 30,
+  kHVCurveTo = 31,
+  kAnd = (12 << 8) + 3,
+  kOr = (12 << 8) + 4,
+  kNot = (12 << 8) + 5,
+  kAbs = (12 << 8) + 9,
+  kAdd = (12 << 8) + 10,
+  kSub = (12 << 8) + 11,
+  kDiv = (12 << 8) + 12,
+  kNeg = (12 << 8) + 14,
+  kEq = (12 << 8) + 15,
+  kDrop = (12 << 8) + 18,
+  kPut = (12 << 8) + 20,
+  kGet = (12 << 8) + 21,
+  kIfElse = (12 << 8) + 22,
+  kRandom = (12 << 8) + 23,
+  kMul = (12 << 8) + 24,
+  kSqrt = (12 << 8) + 26,
+  kDup = (12 << 8) + 27,
+  kExch = (12 << 8) + 28,
+  kIndex = (12 << 8) + 29,
+  kRoll = (12 << 8) + 30,
+  kHFlex = (12 << 8) + 34,
+  kFlex = (12 << 8) + 35,
+  kHFlex1 = (12 << 8) + 36,
+  kFlex1 = (12 << 8) + 37,
+  // Operators that are obsoleted or undocumented, such as 'blend', will be
+  // rejected.
+};
+
 }  // namespace ots
 
 #endif  // OTS_CFF_TYPE2_CHARSTRING_H_
diff --git a/test/SConstruct b/test/SConstruct
index eea29a1..c45f43f 100644
--- a/test/SConstruct
+++ b/test/SConstruct
@@ -8,7 +8,7 @@
 
 # Since the validator-checker tool might handle malicious font files, all hardening options for recent g++/ld are enabled just in case.
 # See http://wiki.debian.org/Hardening for details.
-env = Environment(CCFLAGS = ['-O2', '-I../include', '-I/usr/include/freetype2', '-ggdb', '-Wall', '-W', '-Wno-unused-parameter', '-fno-strict-aliasing', '-fPIE', '-fstack-protector', '-D_FORTIFY_SOURCE=2', '-DOTS_DEBUG'], LINKFLAGS = ['-ggdb', '-Wl,-z,relro', '-Wl,-z,now', '-pie', '-lz'])
+env = Environment(CCFLAGS = ['-O2', '-I../include', '-I../src', '-I/usr/include/freetype2', '-ggdb', '-Wall', '-W', '-Wno-unused-parameter', '-fno-strict-aliasing', '-fPIE', '-fstack-protector', '-D_FORTIFY_SOURCE=2', '-DOTS_DEBUG'], LINKFLAGS = ['-ggdb', '-Wl,-z,relro', '-Wl,-z,now', '-pie', '-lz'])
 # TODO(yusukes): better to use pkg-config freetype2 --cflags
  
 env.Library('../src/libots.a',
@@ -35,7 +35,8 @@
 	    '../src/vdmx.cc',
 	    '../src/vorg.cc'
             ])
- 
+
+env.Program('../test/cff_type2_charstring_test.cc', LIBS = ['ots', 'gtest_main'], LIBPATH = '../src')
 env.Program('../test/ot-sanitise.cc', LIBS = ['ots'], LIBPATH='../src')
 env.Program('../test/idempotent.cc', LIBS = ['ots', 'freetype', 'z', 'm'], LIBPATH='../src')
 env.Program('../test/perf.cc', LIBS = ['ots'], LIBPATH='../src')
diff --git a/test/cff_type2_charstring_test.cc b/test/cff_type2_charstring_test.cc
new file mode 100644
index 0000000..a2a76cf
--- /dev/null
+++ b/test/cff_type2_charstring_test.cc
@@ -0,0 +1,1588 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cff_type2_charstring.h"
+
+#include <gtest/gtest.h>
+
+#include <climits>
+#include <vector>
+
+#include "cff.h"
+
+// Returns a biased number for callsubr and callgsubr operators.
+#define GET_SUBR_NUMBER(n) ((n) - 107)
+#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
+
+namespace {
+
+// A constant which is used in AddSubr function below.
+const int kOpPrefix = INT_MAX;
+
+// Encodes an operator |op| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds.
+bool EncodeOperator(int op, std::vector<uint8_t> *out_bytes) {
+  if (op < 0) {
+    return false;
+  }
+  if (op <= 11) {
+    out_bytes->push_back(op);
+    return true;
+  }
+  if (op == 12) {
+    return false;
+  }
+  if (op <= 27) {
+    out_bytes->push_back(op);
+    return true;
+  }
+  if (op == 28) {
+    return false;
+  }
+  if (op <= 31) {
+    out_bytes->push_back(op);
+    return true;
+  }
+
+  const uint8_t upper = (op & 0xff00u) >> 8;
+  const uint8_t lower = op & 0xffu;
+  if (upper != 12) {
+    return false;
+  }
+  out_bytes->push_back(upper);
+  out_bytes->push_back(lower);
+  return true;
+}
+
+// Encodes a number |num| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds. The function does not support 16.16
+// Fixed number.
+bool EncodeNumber(int num, std::vector<uint8_t> *out_bytes) {
+  if (num >= -107 && num <= 107) {
+    out_bytes->push_back(num + 139);
+    return true;
+  }
+  if (num >= 108 && num <= 1131) {
+    const uint8_t v = ((num - 108) / 256) + 247;
+    const uint8_t w = (num - 108) % 256;
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  if (num <= -108 && num >= -1131) {
+    const uint8_t v = (-(num + 108) / 256) + 251;
+    const uint8_t w = -(num + 108) % 256;
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  if (num <= -32768 && num >= -32767) {
+    const uint8_t v = (num % 0xff00u) >> 8;
+    const uint8_t w = num % 0xffu;
+    out_bytes->push_back(28);
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  return false;
+}
+
+// Adds a subroutine |subr| to |out_buffer| and |out_subr|. The contents of the
+// subroutine is copied to |out_buffer|, and then the position of the subroutine
+// in |out_buffer| is written to |out_subr|. Returns true on success.
+bool AddSubr(const int *subr, size_t subr_len,
+             std::vector<uint8_t>* out_buffer, ots::CFFIndex *out_subr) {
+  size_t pre_offset = out_buffer->size();
+  for (size_t i = 0; i < subr_len; ++i) {
+    if (subr[i] != kOpPrefix) {
+      if (!EncodeNumber(subr[i], out_buffer)) {
+        return false;
+      }
+    } else {
+      if (i + 1 == subr_len) {
+        return false;
+      }
+      ++i;
+      if (!EncodeOperator(subr[i], out_buffer)) {
+        return false;
+      }
+    }
+  }
+
+  ++(out_subr->count);
+  out_subr->off_size = 1;
+  if (out_subr->offsets.empty()) {
+    out_subr->offsets.push_back(pre_offset);
+  }
+  out_subr->offsets.push_back(out_buffer->size());
+  return true;
+}
+
+// Validates |char_string| and returns true if it's valid.
+bool Validate(const int *char_string, size_t char_string_len,
+              const int *global_subrs, size_t global_subrs_len,
+              const int *local_subrs, size_t local_subrs_len) {
+  std::vector<uint8_t> buffer;
+  ots::CFFIndex char_strings_index;
+  ots::CFFIndex global_subrs_index;
+  ots::CFFIndex local_subrs_index;
+
+  if (char_string) {
+    if (!AddSubr(char_string, char_string_len,
+                 &buffer, &char_strings_index)) {
+      return false;
+    }
+  }
+  if (global_subrs) {
+    if (!AddSubr(global_subrs, global_subrs_len,
+                 &buffer, &global_subrs_index)) {
+      return false;
+    }
+  }
+  if (local_subrs) {
+    if (!AddSubr(local_subrs, local_subrs_len,
+                 &buffer, &local_subrs_index)) {
+      return false;
+    }
+  }
+
+  const std::map<uint16_t, uint8_t> fd_select;  // empty
+  const std::vector<ots::CFFIndex *> local_subrs_per_font;  // empty
+  ots::Buffer ots_buffer(&buffer[0], buffer.size());
+
+  return ots::ValidateType2CharStringIndex(char_strings_index,
+                                           global_subrs_index,
+                                           fd_select,
+                                           local_subrs_per_font,
+                                           &local_subrs_index,
+                                           &ots_buffer);
+}
+
+// Validates |char_string| and returns true if it's valid.
+bool ValidateCharStrings(const int *char_string, size_t char_string_len) {
+  return Validate(char_string, char_string_len, NULL, 0, NULL, 0);
+}
+
+}  // namespace
+
+TEST(ValidateTest, TestRMoveTo) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      1, 2, 3, kOpPrefix, ots::kRMoveTo,  // invalid number of args
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHMoveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, kOpPrefix, ots::kHMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHMoveTo,
+      1, 2, kOpPrefix, ots::kHMoveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVMoveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, kOpPrefix, ots::kVMoveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, kOpPrefix, ots::kRLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kRLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kRLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHLineTo,
+      1, 2, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, 4, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVLineTo,
+      1, 2, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, 4, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kVLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRRCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kRRCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kRRCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHHCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kHHCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHHCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kHHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHVCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      // The first form.
+      1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      kOpPrefix, ots::kHVCurveTo,
+      // The second form.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+      kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      22, 23, 24, 25, kOpPrefix, ots::kHVCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kHVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRCurveLine) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+      kOpPrefix, ots::kRCurveLine,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRCurveLine,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      // can't be the first op.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRLineCurve) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kRLineCurve,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRLineCurve,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      // can't be the first op.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVHCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      // The first form.
+      1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      kOpPrefix, ots::kVHCurveTo,
+      // The second form.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+      kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      22, 23, 24, 25, kOpPrefix, ots::kVHCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kVHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVVCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVVCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kVVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestFlex) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHFlex) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kHFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHFlex1) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestFlex1) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestEndChar) {
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         NULL, 0,
+                         local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         global_subrs, ARRAYSIZE(global_subrs),
+                         NULL, 0));
+  }
+}
+
+TEST(ValidateTest, TestHStem) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kHStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVStem) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kVStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHStemHm) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kHStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVStemHm) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kVStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHintMask) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kHintMask, 0x00,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, 6, kOpPrefix, ots::kHintMask, 0x00,  // vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kHintMask, 0x00,  // no stems to mask
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, kOpPrefix, ots::kHintMask, 0x00,  // invalid vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCntrMask) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kCntrMask, 0x00,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, 6, kOpPrefix, ots::kCntrMask, 0x00,  // vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kCntrMask, 0x00,  // no stems to mask
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, kOpPrefix, ots::kCntrMask, 0x00,  // invalid vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAbs) {
+  {
+    const int char_string[] = {
+      -1, kOpPrefix, ots::kAbs,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kAbs,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAdd) {
+  {
+    const int char_string[] = {
+      0, 1, kOpPrefix, ots::kAdd,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kAdd,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestSub) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kSub,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kSub,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDiv) {
+  // TODO(yusukes): Test div-by-zero.
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kDiv,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kDiv,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestNeg) {
+  {
+    const int char_string[] = {
+      -1, kOpPrefix, ots::kNeg,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kNeg,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRandom) {
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kRandom,  // OTS rejects the operator.
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestMul) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kMul,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kMul,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestSqrt) {
+  // TODO(yusukes): Test negative numbers.
+  {
+    const int char_string[] = {
+      4, kOpPrefix, ots::kSqrt,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kSqrt,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDrop) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDrop,
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kDrop,  // invalid
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestExch) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDup,
+      kOpPrefix, ots::kExch,
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kExch,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestIndex) {
+  {
+    const int char_string[] = {
+      1, 2, 3, -1, kOpPrefix, ots::kIndex,  // OTS rejects the operator.
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRoll) {
+  {
+    const int char_string[] = {
+      1, 2, 2, 1, kOpPrefix, ots::kRoll,  // OTS rejects the operator.
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDup) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDup,
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kDup,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestPut) {
+  {
+    const int char_string[] = {
+      1, 10, kOpPrefix, ots::kPut,  // OTS rejects the operator.
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestGet) {
+  {
+    const int char_string[] = {
+      1, 10, kOpPrefix, ots::kGet,  // OTS rejects the operator.
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAnd) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kAnd,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kAnd,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestOr) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kOr,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kOr,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestNot) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kNot,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kNot,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestEq) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kEq,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kEq,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestIfElse) {
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kIfElse,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, kOpPrefix, ots::kIfElse,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallSubr) {
+  // Call valid subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         NULL, 0,
+                         local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  // Call undefined subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallGSubr) {
+  // Call valid subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         global_subrs, ARRAYSIZE(global_subrs),
+                         NULL, 0));
+  }
+  // Call undefined subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallGSubrWithComputedValues) {
+  {
+    // OTS does not allow to call(g)subr with a subroutine number which is
+    // not a immediate value for safety.
+    const int char_string[] = {
+      0, 0, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+}
+
+TEST(ValidateTest, TestInfiniteLoop) {
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  // mutual recursion which doesn't stop.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int global_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+}
+
+TEST(ValidateTest, TestStackOverflow) {
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9,  // overflow
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDeprecatedOperators) {
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 0,  // dotsection operator, which is not supported.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, 16,  // 'blend'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 8,  // 'store'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 13,  // 'load'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestUnterminatedCharString) {
+  // No endchar operator.
+  {
+    const int char_string[] = {
+      123,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      123, 456,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      123, 456, kOpPrefix, ots::kReturn,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}