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)));
+ }
+}