diff --git a/src/port.h b/src/port.h index fd5498e..4906fbc 100644 --- a/src/port.h +++ b/src/port.h @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// Helper function for bit twiddling +// Helper function for bit twiddling and macros for branch prediction. #ifndef WOFF2_PORT_H_ #define WOFF2_PORT_H_ @@ -43,4 +43,19 @@ inline int Log2Floor(uint32 n) { } } // namespace woff2 + +/* Compatibility with non-clang compilers. */ +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || \ + (defined(__llvm__) && __has_builtin(__builtin_expect)) +#define PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +#define PREDICT_FALSE(x) (x) +#define PREDICT_TRUE(x) (x) +#endif + #endif // WOFF2_PORT_H_ diff --git a/src/store_bytes.h b/src/store_bytes.h index a9a3401..c34e43f 100644 --- a/src/store_bytes.h +++ b/src/store_bytes.h @@ -33,8 +33,15 @@ inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) { } inline size_t Store16(uint8_t* dst, size_t offset, int x) { +#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + *reinterpret_cast(dst + offset) = + ((x & 0xFF) << 8) | ((x & 0xFF00) >> 8); +#elif (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + *reinterpret_cast(dst + offset) = reinterpret_cast(x); +#else dst[offset] = x >> 8; dst[offset + 1] = x; +#endif return offset + 2; } @@ -46,8 +53,18 @@ inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) { } inline void Store16(int val, size_t* offset, uint8_t* dst) { +#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + *reinterpret_cast(dst + *offset) = + ((val & 0xFF) << 8) | ((val & 0xFF00) >> 8); + *offset += 2; +#elif (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + *reinterpret_cast(dst + *offset) = + reinterpret_cast(val); + *offset += 2; +#else dst[(*offset)++] = val >> 8; dst[(*offset)++] = val; +#endif } inline void StoreBytes(const uint8_t* data, size_t len, diff --git a/src/woff2_common.cc b/src/woff2_common.cc index de5998b..39dc54e 100644 --- a/src/woff2_common.cc +++ b/src/woff2_common.cc @@ -24,9 +24,16 @@ namespace woff2 { uint32_t ComputeULongSum(const uint8_t* buf, size_t size) { uint32_t checksum = 0; for (size_t i = 0; i < size; i += 4) { - // We assume the addition is mod 2^32, which is valid because unsigned +#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + uint32_t v = *reinterpret_cast(buf + i); + checksum += (((v & 0xFF) << 24) | ((v & 0xFF00) << 8) | + ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24)); +#elif (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + checksum += *reinterpret_cast(buf + i); +#else checksum += (buf[i] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | buf[i + 3]; +#endif } return checksum; } diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index 1ee72b2..125f578 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -24,9 +24,11 @@ #include #include #include +#include #include "./buffer.h" #include "./decode.h" +#include "./port.h" #include "./round.h" #include "./store_bytes.h" #include "./table_tags.h" @@ -76,12 +78,11 @@ int WithSign(int flag, int baseval) { } bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, - unsigned int n_points, std::vector* result, - size_t* in_bytes_consumed) { + unsigned int n_points, Point* result, size_t* in_bytes_consumed) { int x = 0; int y = 0; - if (n_points > in_size) { + if (PREDICT_FALSE(n_points > in_size)) { return FONT_COMPRESSION_FAILURE(); } unsigned int triplet_index = 0; @@ -100,8 +101,8 @@ bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, } else { n_data_bytes = 4; } - if (triplet_index + n_data_bytes > in_size || - triplet_index + n_data_bytes < triplet_index) { + if (PREDICT_FALSE(triplet_index + n_data_bytes > in_size || + triplet_index + n_data_bytes < triplet_index)) { return FONT_COMPRESSION_FAILURE(); } int dx, dy; @@ -134,11 +135,7 @@ bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, // Possible overflow but coordinate values are not security sensitive x += dx; y += dy; - result->push_back(Point()); - Point& back = result->back(); - back.x = x; - back.y = y; - back.on_curve = on_curve; + *result++ = {x, y, on_curve}; } *in_bytes_consumed = triplet_index; return true; @@ -146,7 +143,7 @@ bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, // This function stores just the point data. On entry, dst points to the // beginning of a simple glyph. Returns true on success. -bool StorePoints(const std::vector& points, +bool StorePoints(unsigned int n_points, const Point* points, unsigned int n_contours, unsigned int instruction_length, uint8_t* dst, size_t dst_size, size_t* glyph_size) { // I believe that n_contours < 65536, in which case this is safe. However, a @@ -160,7 +157,7 @@ bool StorePoints(const std::vector& points, unsigned int x_bytes = 0; unsigned int y_bytes = 0; - for (unsigned int i = 0; i < points.size(); ++i) { + for (unsigned int i = 0; i < n_points; ++i) { const Point& point = points[i]; int flag = point.on_curve ? kGlyfOnCurve : 0; int dx = point.x - last_x; @@ -187,12 +184,12 @@ bool StorePoints(const std::vector& points, repeat_count++; } else { if (repeat_count != 0) { - if (flag_offset >= dst_size) { + if (PREDICT_FALSE(flag_offset >= dst_size)) { return FONT_COMPRESSION_FAILURE(); } dst[flag_offset++] = repeat_count; } - if (flag_offset >= dst_size) { + if (PREDICT_FALSE(flag_offset >= dst_size)) { return FONT_COMPRESSION_FAILURE(); } dst[flag_offset++] = flag; @@ -204,15 +201,15 @@ bool StorePoints(const std::vector& points, } if (repeat_count != 0) { - if (flag_offset >= dst_size) { + if (PREDICT_FALSE(flag_offset >= dst_size)) { return FONT_COMPRESSION_FAILURE(); } dst[flag_offset++] = repeat_count; } unsigned int xy_bytes = x_bytes + y_bytes; - if (xy_bytes < x_bytes || + if (PREDICT_FALSE(xy_bytes < x_bytes || flag_offset + xy_bytes < flag_offset || - flag_offset + xy_bytes > dst_size) { + flag_offset + xy_bytes > dst_size)) { return FONT_COMPRESSION_FAILURE(); } @@ -220,7 +217,7 @@ bool StorePoints(const std::vector& points, int y_offset = flag_offset + x_bytes; last_x = 0; last_y = 0; - for (unsigned int i = 0; i < points.size(); ++i) { + for (unsigned int i = 0; i < n_points; ++i) { int dx = points[i].x - last_x; if (dx == 0) { // pass @@ -247,19 +244,25 @@ bool StorePoints(const std::vector& points, // Compute the bounding box of the coordinates, and store into a glyf buffer. // A precondition is that there are at least 10 bytes available. -void ComputeBbox(const std::vector& points, uint8_t* dst) { +void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { int x_min = 0; int y_min = 0; int x_max = 0; int y_max = 0; - for (unsigned int i = 0; i < points.size(); ++i) { + if (n_points > 0) { + x_min = points[0].x; + x_max = points[0].x; + y_min = points[0].y; + y_max = points[0].y; + } + for (unsigned int i = 1; i < n_points; ++i) { int x = points[i].x; int y = points[i].y; - if (i == 0 || x < x_min) x_min = x; - if (i == 0 || x > x_max) x_max = x; - if (i == 0 || y < y_min) y_min = y; - if (i == 0 || y > y_max) y_max = y; + x_min = std::min(x, x_min); + x_max = std::max(x, x_max); + y_min = std::min(y, y_min); + y_max = std::max(y, y_max); } size_t offset = 2; offset = Store16(dst, offset, x_min); @@ -274,25 +277,26 @@ bool ProcessBboxStream(Buffer* bbox_stream, unsigned int n_glyphs, const std::vector& loca_values, uint8_t* glyf_buf, size_t glyf_buf_length) { const uint8_t* buf = bbox_stream->buffer(); - if (n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1) { + if (PREDICT_FALSE(n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1)) { return FONT_COMPRESSION_FAILURE(); } // Safe because n_glyphs is bounded unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2; - if (!bbox_stream->Skip(bitmap_length)) { + if (PREDICT_FALSE(!bbox_stream->Skip(bitmap_length))) { return FONT_COMPRESSION_FAILURE(); } for (unsigned int i = 0; i < n_glyphs; ++i) { if (buf[i >> 3] & (0x80 >> (i & 7))) { uint32_t loca_offset = loca_values[i]; - if (loca_values[i + 1] - loca_offset < kEndPtsOfContoursOffset) { + if (PREDICT_FALSE( + loca_values[i + 1] - loca_offset < kEndPtsOfContoursOffset)) { return FONT_COMPRESSION_FAILURE(); } - if (glyf_buf_length < 2 + 10 || - loca_offset > glyf_buf_length - 2 - 10) { + if (PREDICT_FALSE(glyf_buf_length < 2 + 10 || + loca_offset > glyf_buf_length - 2 - 10)) { return FONT_COMPRESSION_FAILURE(); } - if (!bbox_stream->Read(glyf_buf + loca_offset + 2, 8)) { + if (PREDICT_FALSE(!bbox_stream->Read(glyf_buf + loca_offset + 2, 8))) { return FONT_COMPRESSION_FAILURE(); } } @@ -307,7 +311,7 @@ bool ProcessComposite(Buffer* composite_stream, uint8_t* dst, uint16_t flags = FLAG_MORE_COMPONENTS; while (flags & FLAG_MORE_COMPONENTS) { - if (!composite_stream->ReadU16(&flags)) { + if (PREDICT_FALSE(!composite_stream->ReadU16(&flags))) { return FONT_COMPRESSION_FAILURE(); } we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0; @@ -324,12 +328,12 @@ bool ProcessComposite(Buffer* composite_stream, uint8_t* dst, } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) { arg_size += 8; } - if (!composite_stream->Skip(arg_size)) { + if (PREDICT_FALSE(!composite_stream->Skip(arg_size))) { return FONT_COMPRESSION_FAILURE(); } } size_t composite_glyph_size = composite_stream->offset() - start_offset; - if (composite_glyph_size + kCompositeGlyphBegin > dst_size) { + if (PREDICT_FALSE(composite_glyph_size + kCompositeGlyphBegin > dst_size)) { return FONT_COMPRESSION_FAILURE(); } Store16(dst, 0, 0xffff); // nContours = -1 for composite glyph @@ -346,10 +350,10 @@ bool StoreLoca(const std::vector& loca_values, int index_format, uint8_t* dst, size_t dst_size) { const uint64_t loca_size = loca_values.size(); const uint64_t offset_size = index_format ? 4 : 2; - if ((loca_size << 2) >> 2 != loca_size) { + if (PREDICT_FALSE((loca_size << 2) >> 2 != loca_size)) { return FONT_COMPRESSION_FAILURE(); } - if (offset_size * loca_size > dst_size) { + if (PREDICT_FALSE(offset_size * loca_size > dst_size)) { return FONT_COMPRESSION_FAILURE(); } size_t offset = 0; @@ -373,27 +377,27 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size, uint32_t version; std::vector > substreams(kNumSubStreams); - if (!file.ReadU32(&version)) { + if (PREDICT_FALSE(!file.ReadU32(&version))) { return FONT_COMPRESSION_FAILURE(); } uint16_t num_glyphs; uint16_t index_format; - if (!file.ReadU16(&num_glyphs) || - !file.ReadU16(&index_format)) { + if (PREDICT_FALSE(!file.ReadU16(&num_glyphs) || + !file.ReadU16(&index_format))) { return FONT_COMPRESSION_FAILURE(); } unsigned int offset = (2 + kNumSubStreams) * 4; - if (offset > data_size) { + if (PREDICT_FALSE(offset > data_size)) { return FONT_COMPRESSION_FAILURE(); } // Invariant from here on: data_size >= offset for (int i = 0; i < kNumSubStreams; ++i) { uint32_t substream_size; - if (!file.ReadU32(&substream_size)) { + if (PREDICT_FALSE(!file.ReadU32(&substream_size))) { return FONT_COMPRESSION_FAILURE(); } - if (substream_size > data_size - offset) { + if (PREDICT_FALSE(substream_size > data_size - offset)) { return FONT_COMPRESSION_FAILURE(); } substreams[i] = std::make_pair(data + offset, substream_size); @@ -409,12 +413,13 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size, std::vector loca_values(num_glyphs + 1); std::vector n_points_vec; - std::vector points; + std::unique_ptr points; + size_t points_size = 0; uint32_t loca_offset = 0; for (unsigned int i = 0; i < num_glyphs; ++i) { size_t glyph_size = 0; uint16_t n_contours = 0; - if (!n_contour_stream.ReadU16(&n_contours)) { + if (PREDICT_FALSE(!n_contour_stream.ReadU16(&n_contours))) { return FONT_COMPRESSION_FAILURE(); } uint8_t* glyf_dst = dst + loca_offset; @@ -423,20 +428,20 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size, // composite glyph bool have_instructions = false; unsigned int instruction_size = 0; - if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size, - &glyph_size, &have_instructions)) { + if (PREDICT_FALSE(!ProcessComposite(&composite_stream, glyf_dst, + glyf_dst_size, &glyph_size, &have_instructions))) { return FONT_COMPRESSION_FAILURE(); } if (have_instructions) { - if (!Read255UShort(&glyph_stream, &instruction_size)) { + if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { return FONT_COMPRESSION_FAILURE(); } - if (instruction_size + 2 > glyf_dst_size - glyph_size) { + if (PREDICT_FALSE(instruction_size + 2 > glyf_dst_size - glyph_size)) { return FONT_COMPRESSION_FAILURE(); } Store16(glyf_dst, glyph_size, instruction_size); - if (!instruction_stream.Read(glyf_dst + glyph_size + 2, - instruction_size)) { + if (PREDICT_FALSE(!instruction_stream.Read(glyf_dst + glyph_size + 2, + instruction_size))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += instruction_size + 2; @@ -444,21 +449,22 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size, } else if (n_contours > 0) { // simple glyph n_points_vec.clear(); - points.clear(); unsigned int total_n_points = 0; unsigned int n_points_contour; for (unsigned int j = 0; j < n_contours; ++j) { - if (!Read255UShort(&n_points_stream, &n_points_contour)) { + if (PREDICT_FALSE( + !Read255UShort(&n_points_stream, &n_points_contour))) { return FONT_COMPRESSION_FAILURE(); } n_points_vec.push_back(n_points_contour); - if (total_n_points + n_points_contour < total_n_points) { + if (PREDICT_FALSE(total_n_points + n_points_contour < total_n_points)) { return FONT_COMPRESSION_FAILURE(); } total_n_points += n_points_contour; } unsigned int flag_size = total_n_points; - if (flag_size > flag_stream.length() - flag_stream.offset()) { + if (PREDICT_FALSE( + flag_size > flag_stream.length() - flag_stream.offset())) { return FONT_COMPRESSION_FAILURE(); } const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset(); @@ -466,58 +472,63 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size, glyph_stream.offset(); size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); size_t triplet_bytes_consumed = 0; - if (!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points, - &points, &triplet_bytes_consumed)) { + if (points_size < total_n_points) { + points_size = total_n_points; + points.reset(new Point[points_size]); + } + if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, triplet_size, + total_n_points, points.get(), &triplet_bytes_consumed))) { return FONT_COMPRESSION_FAILURE(); } const uint32_t header_and_endpts_contours_size = kEndPtsOfContoursOffset + 2 * n_contours; - if (glyf_dst_size < header_and_endpts_contours_size) { + if (PREDICT_FALSE(glyf_dst_size < header_and_endpts_contours_size)) { return FONT_COMPRESSION_FAILURE(); } Store16(glyf_dst, 0, n_contours); - ComputeBbox(points, glyf_dst); + ComputeBbox(total_n_points, points.get(), glyf_dst); size_t offset = kEndPtsOfContoursOffset; int end_point = -1; for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) { end_point += n_points_vec[contour_ix]; - if (end_point >= 65536) { + if (PREDICT_FALSE(end_point >= 65536)) { return FONT_COMPRESSION_FAILURE(); } offset = Store16(glyf_dst, offset, end_point); } - if (!flag_stream.Skip(flag_size)) { + if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { return FONT_COMPRESSION_FAILURE(); } - if (!glyph_stream.Skip(triplet_bytes_consumed)) { + if (PREDICT_FALSE(!glyph_stream.Skip(triplet_bytes_consumed))) { return FONT_COMPRESSION_FAILURE(); } unsigned int instruction_size; - if (!Read255UShort(&glyph_stream, &instruction_size)) { + if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { return FONT_COMPRESSION_FAILURE(); } - if (glyf_dst_size - header_and_endpts_contours_size < - instruction_size + 2) { + if (PREDICT_FALSE(glyf_dst_size - header_and_endpts_contours_size < + instruction_size + 2)) { return FONT_COMPRESSION_FAILURE(); } uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size; Store16(instruction_dst, 0, instruction_size); - if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) { + if (PREDICT_FALSE( + !instruction_stream.Read(instruction_dst + 2, instruction_size))) { return FONT_COMPRESSION_FAILURE(); } - if (!StorePoints(points, n_contours, instruction_size, - glyf_dst, glyf_dst_size, &glyph_size)) { + if (PREDICT_FALSE(!StorePoints(total_n_points, points.get(), n_contours, + instruction_size, glyf_dst, glyf_dst_size, &glyph_size))) { return FONT_COMPRESSION_FAILURE(); } } else { glyph_size = 0; } loca_values[i] = loca_offset; - if (glyph_size + 3 < glyph_size) { + if (PREDICT_FALSE(glyph_size + 3 < glyph_size)) { return FONT_COMPRESSION_FAILURE(); } glyph_size = Round4(glyph_size); - if (glyph_size > dst_size - loca_offset) { + if (PREDICT_FALSE(glyph_size > dst_size - loca_offset)) { // This shouldn't happen, but this test defensively maintains the // invariant that loca_offset <= dst_size. return FONT_COMPRESSION_FAILURE(); @@ -525,8 +536,8 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size, loca_offset += glyph_size; } loca_values[num_glyphs] = loca_offset; - if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values, - dst, dst_size)) { + if (PREDICT_FALSE(!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values, + dst, dst_size))) { return FONT_COMPRESSION_FAILURE(); } return StoreLoca(loca_values, index_format, loca_buf, loca_size); @@ -548,15 +559,15 @@ const Table* FindTable(const std::vector& tables, uint32_t tag) { bool ReconstructTransformedGlyf(const uint8_t* transformed_buf, size_t transformed_size, const Table* glyf_table, const Table* loca_table, uint8_t* dst, size_t dst_length) { - if (glyf_table == NULL || loca_table == NULL) { + if (PREDICT_FALSE(glyf_table == NULL || loca_table == NULL)) { return FONT_COMPRESSION_FAILURE(); } - if (static_cast(glyf_table->dst_offset + glyf_table->dst_length) > - dst_length) { + if (PREDICT_FALSE(static_cast(glyf_table->dst_offset + + glyf_table->dst_length) > dst_length)) { return FONT_COMPRESSION_FAILURE(); } - if (static_cast(loca_table->dst_offset + loca_table->dst_length) > - dst_length) { + if (PREDICT_FALSE(static_cast(loca_table->dst_offset + + loca_table->dst_length) > dst_length)) { return FONT_COMPRESSION_FAILURE(); } return ReconstructGlyf(transformed_buf, transformed_size, @@ -574,7 +585,7 @@ bool ReconstructTransformed(const std::vector
& tables, uint32_t tag, glyf_table, loca_table, dst, dst_length); } else if (tag == kLocaTableTag) { // processing was already done by glyf table, but validate - if (!FindTable(tables, kGlyfTableTag)) { + if (PREDICT_FALSE(!FindTable(tables, kGlyfTableTag))) { return FONT_COMPRESSION_FAILURE(); } } else { @@ -606,8 +617,8 @@ bool FixCollectionChecksums(size_t header_version, const std::vector& table_indices = ttc_font.table_indices; const Table* head_table = FindTable(ttc_font, tables, kHeadTableTag); - if (head_table == NULL || - head_table->dst_length < kCheckSumAdjustmentOffset + 4) { + if (PREDICT_FALSE(head_table == NULL || + head_table->dst_length < kCheckSumAdjustmentOffset + 4)) { return FONT_COMPRESSION_FAILURE(); } @@ -652,8 +663,8 @@ bool FixCollectionChecksums(size_t header_version, bool FixChecksums(const std::vector
& tables, uint8_t* dst) { const Table* head_table = FindTable(tables, kHeadTableTag); - if (head_table == NULL || - head_table->dst_length < kCheckSumAdjustmentOffset + 4) { + if (PREDICT_FALSE(head_table == NULL || + head_table->dst_length < kCheckSumAdjustmentOffset + 4)) { return FONT_COMPRESSION_FAILURE(); } size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset; @@ -677,7 +688,7 @@ bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, size_t uncompressed_size = dst_size; int ok = BrotliDecompressBuffer(src_size, src_buf, &uncompressed_size, dst_buf); - if (!ok || uncompressed_size != dst_size) { + if (PREDICT_FALSE(!ok || uncompressed_size != dst_size)) { return FONT_COMPRESSION_FAILURE(); } return true; @@ -688,19 +699,19 @@ bool ReadTableDirectory(Buffer* file, std::vector
* tables, for (size_t i = 0; i < num_tables; ++i) { Table* table = &(*tables)[i]; uint8_t flag_byte; - if (!file->ReadU8(&flag_byte)) { + if (PREDICT_FALSE(!file->ReadU8(&flag_byte))) { return FONT_COMPRESSION_FAILURE(); } uint32_t tag; if ((flag_byte & 0x3f) == 0x3f) { - if (!file->ReadU32(&tag)) { + if (PREDICT_FALSE(!file->ReadU32(&tag))) { return FONT_COMPRESSION_FAILURE(); } } else { tag = kKnownTags[flag_byte & 0x3f]; } // Bits 6 and 7 are reserved and must be 0. - if ((flag_byte & 0xC0) != 0) { + if (PREDICT_FALSE((flag_byte & 0xC0)) != 0) { return FONT_COMPRESSION_FAILURE(); } uint32_t flags = 0; @@ -712,15 +723,15 @@ bool ReadTableDirectory(Buffer* file, std::vector
* tables, flags |= kWoff2FlagsTransform; } uint32_t dst_length; - if (!ReadBase128(file, &dst_length)) { + if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { return FONT_COMPRESSION_FAILURE(); } uint32_t transform_length = dst_length; if ((flags & kWoff2FlagsTransform) != 0) { - if (!ReadBase128(file, &transform_length)) { + if (PREDICT_FALSE(!ReadBase128(file, &transform_length))) { return FONT_COMPRESSION_FAILURE(); } - if (tag == kLocaTableTag && transform_length) { + if (PREDICT_FALSE(tag == kLocaTableTag && transform_length)) { return FONT_COMPRESSION_FAILURE(); } } @@ -793,62 +804,65 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, uint32_t signature; uint32_t flavor; - if (!file.ReadU32(&signature) || signature != kWoff2Signature || - !file.ReadU32(&flavor)) { + if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || + !file.ReadU32(&flavor))) { return FONT_COMPRESSION_FAILURE(); } // TODO(user): Should call IsValidVersionTag() here. uint32_t reported_length; - if (!file.ReadU32(&reported_length) || length != reported_length) { + if (PREDICT_FALSE( + !file.ReadU32(&reported_length) || length != reported_length)) { return FONT_COMPRESSION_FAILURE(); } uint16_t num_tables; - if (!file.ReadU16(&num_tables) || !num_tables) { + if (PREDICT_FALSE(!file.ReadU16(&num_tables) || !num_tables)) { return FONT_COMPRESSION_FAILURE(); } // We don't care about these fields of the header: // uint16_t reserved // uint32_t total_sfnt_size, the caller already passes it as result_length - if (!file.Skip(6)) { + if (PREDICT_FALSE(!file.Skip(6))) { return FONT_COMPRESSION_FAILURE(); } uint32_t compressed_length; - if (!file.ReadU32(&compressed_length)) { + if (PREDICT_FALSE(!file.ReadU32(&compressed_length))) { return FONT_COMPRESSION_FAILURE(); } // We don't care about these fields of the header: // uint16_t major_version, minor_version - if (!file.Skip(2 * 2)) { + if (PREDICT_FALSE(!file.Skip(2 * 2))) { return FONT_COMPRESSION_FAILURE(); } uint32_t meta_offset; uint32_t meta_length; uint32_t meta_length_orig; - if (!file.ReadU32(&meta_offset) || + if (PREDICT_FALSE(!file.ReadU32(&meta_offset) || !file.ReadU32(&meta_length) || - !file.ReadU32(&meta_length_orig)) { + !file.ReadU32(&meta_length_orig))) { return FONT_COMPRESSION_FAILURE(); } if (meta_offset) { - if (meta_offset >= length || length - meta_offset < meta_length) { + if (PREDICT_FALSE( + meta_offset >= length || length - meta_offset < meta_length)) { return FONT_COMPRESSION_FAILURE(); } } uint32_t priv_offset; uint32_t priv_length; - if (!file.ReadU32(&priv_offset) || - !file.ReadU32(&priv_length)) { + if (PREDICT_FALSE(!file.ReadU32(&priv_offset) || + !file.ReadU32(&priv_length))) { return FONT_COMPRESSION_FAILURE(); } if (priv_offset) { - if (priv_offset >= length || length - priv_offset < priv_length) { + if (PREDICT_FALSE( + priv_offset >= length || length - priv_offset < priv_length)) { return FONT_COMPRESSION_FAILURE(); } } std::vector
tables(num_tables); - if (!ReadTableDirectory(&file, &tables, num_tables)) { + if (PREDICT_FALSE(!ReadTableDirectory(&file, &tables, num_tables))) { return FONT_COMPRESSION_FAILURE(); } @@ -858,11 +872,11 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, std::map loca_by_glyf; if (flavor == kTtcFontFlavor) { - if (!file.ReadU32(&header_version)) { + if (PREDICT_FALSE(!file.ReadU32(&header_version))) { return FONT_COMPRESSION_FAILURE(); } uint32_t num_fonts; - if (!Read255UShort(&file, &num_fonts) || !num_fonts) { + if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { return FONT_COMPRESSION_FAILURE(); } ttc_fonts.resize(num_fonts); @@ -870,10 +884,10 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, for (auto i = 0; i < num_fonts; i++) { TtcFont& ttc_font = ttc_fonts[i]; uint32_t num_tables; - if (!Read255UShort(&file, &num_tables) || !num_tables) { + if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { return FONT_COMPRESSION_FAILURE(); } - if (!file.ReadU32(&ttc_font.flavor)) { + if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { return FONT_COMPRESSION_FAILURE(); } @@ -886,7 +900,7 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, for (auto j = 0; j < num_tables; j++) { unsigned int table_idx; - if (!Read255UShort(&file, &table_idx)) { + if (PREDICT_FALSE(!Read255UShort(&file, &table_idx))) { return FONT_COMPRESSION_FAILURE(); } ttc_font.table_indices[j] = table_idx; @@ -903,7 +917,7 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, } - if ((glyf_table == NULL) != (loca_table == NULL)) { + if (PREDICT_FALSE((glyf_table == NULL) != (loca_table == NULL))) { fprintf(stderr, "Cannot have just one of glyf/loca\n"); return FONT_COMPRESSION_FAILURE(); } @@ -917,7 +931,7 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, const uint64_t first_table_offset = ComputeOffsetToFirstTable(header_version, num_tables, ttc_fonts); - if (first_table_offset > result_length) { + if (PREDICT_FALSE(first_table_offset > result_length)) { return FONT_COMPRESSION_FAILURE(); } @@ -931,27 +945,28 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, table->src_offset = src_offset; table->src_length = (i == 0 ? compressed_length : 0); src_offset += table->src_length; - if (src_offset > std::numeric_limits::max()) { + if (PREDICT_FALSE(src_offset > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } src_offset = Round4(src_offset); table->dst_offset = dst_offset; dst_offset += table->dst_length; - if (dst_offset > std::numeric_limits::max()) { + if (PREDICT_FALSE(dst_offset > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } dst_offset = Round4(dst_offset); uncompressed_sum += table->src_length; - if (uncompressed_sum > std::numeric_limits::max()) { + if (PREDICT_FALSE( + uncompressed_sum > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } } // Enforce same 30M limit on uncompressed tables as OTS - if (uncompressed_sum > 30 * 1024 * 1024) { + if (PREDICT_FALSE(uncompressed_sum > 30 * 1024 * 1024)) { return FONT_COMPRESSION_FAILURE(); } - if (src_offset > length || dst_offset != result_length) { + if (PREDICT_FALSE(src_offset > length || dst_offset != result_length)) { fprintf(stderr, "offset fail; src_offset %" PRIu64 " length %lu " "dst_offset %" PRIu64 " result_length %lu\n", src_offset, length, dst_offset, result_length); @@ -977,26 +992,26 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, } if (meta_offset) { - if (src_offset != meta_offset) { + if (PREDICT_FALSE(src_offset != meta_offset)) { return FONT_COMPRESSION_FAILURE(); } src_offset = Round4(meta_offset + meta_length); - if (src_offset > std::numeric_limits::max()) { + if (PREDICT_FALSE(src_offset > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } } if (priv_offset) { - if (src_offset != priv_offset) { + if (PREDICT_FALSE(src_offset != priv_offset)) { return FONT_COMPRESSION_FAILURE(); } src_offset = Round4(priv_offset + priv_length); - if (src_offset > std::numeric_limits::max()) { + if (PREDICT_FALSE(src_offset > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } } - if (src_offset != Round4(length)) { + if (PREDICT_FALSE(src_offset != Round4(length))) { return FONT_COMPRESSION_FAILURE(); } @@ -1051,7 +1066,7 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, const uint8_t* src_buf = data + table->src_offset; size_t transform_length = table->transform_length; if ((flags & kWoff2FlagsContinueStream) != 0) { - if (!continue_valid) { + if (PREDICT_FALSE(!continue_valid)) { return FONT_COMPRESSION_FAILURE(); } } else if ((flags & kWoff2FlagsContinueStream) == 0) { @@ -1061,13 +1076,13 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, break; } total_size += tables[j].transform_length; - if (total_size > std::numeric_limits::max()) { + if (PREDICT_FALSE(total_size > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } } uncompressed_buf.resize(total_size); - if (!Woff2Uncompress(&uncompressed_buf[0], total_size, - src_buf, compressed_length)) { + if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], total_size, + src_buf, compressed_length))) { return FONT_COMPRESSION_FAILURE(); } transform_buf = &uncompressed_buf[0]; @@ -1077,11 +1092,11 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, } if ((flags & kWoff2FlagsTransform) == 0) { - if (transform_length != table->dst_length) { + if (PREDICT_FALSE(transform_length != table->dst_length)) { return FONT_COMPRESSION_FAILURE(); } - if (static_cast(table->dst_offset + transform_length) > - result_length) { + if (PREDICT_FALSE(static_cast(table->dst_offset + + transform_length) > result_length)) { return FONT_COMPRESSION_FAILURE(); } @@ -1091,35 +1106,37 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, if (header_version) { if (table->tag == kGlyfTableTag) { const Table* loca_table = loca_by_glyf[table]; - if (!ReconstructTransformedGlyf(transform_buf, transform_length, - table, loca_table, result, result_length)) { + if (PREDICT_FALSE(!ReconstructTransformedGlyf(transform_buf, + transform_length, table, loca_table, result, result_length))) { return FONT_COMPRESSION_FAILURE(); } - } else if (table->tag != kLocaTableTag) { + } else if (PREDICT_FALSE(table->tag != kLocaTableTag)) { // transform for this tag not known return FONT_COMPRESSION_FAILURE(); } } else { - if (!ReconstructTransformed(tables, table->tag, - transform_buf, transform_length, result, result_length)) { + if (PREDICT_FALSE(!ReconstructTransformed(tables, table->tag, + transform_buf, transform_length, result, result_length))) { return FONT_COMPRESSION_FAILURE(); } } } if (continue_valid) { transform_buf += transform_length; - if (transform_buf > &uncompressed_buf[0] + uncompressed_buf.size()) { + if (PREDICT_FALSE( + transform_buf > &uncompressed_buf[0] + uncompressed_buf.size())) { return FONT_COMPRESSION_FAILURE(); } } } if (header_version) { - if (!FixCollectionChecksums(header_version, tables, ttc_fonts, result)) { + if (PREDICT_FALSE( + !FixCollectionChecksums(header_version, tables, ttc_fonts, result))) { return FONT_COMPRESSION_FAILURE(); } } else { - if (!FixChecksums(tables, result)) { + if (PREDICT_FALSE(!FixChecksums(tables, result))) { return FONT_COMPRESSION_FAILURE(); } } diff --git a/src/woff2_enc.cc b/src/woff2_enc.cc index 230773c..e672ac3 100644 --- a/src/woff2_enc.cc +++ b/src/woff2_enc.cc @@ -47,10 +47,11 @@ const size_t kWoff2EntrySize = 20; bool Compress(const uint8_t* data, const size_t len, uint8_t* result, uint32_t* result_len, - brotli::BrotliParams::Mode mode) { + brotli::BrotliParams::Mode mode, int quality) { size_t compressed_len = *result_len; brotli::BrotliParams params; params.mode = mode; + params.quality = quality; if (brotli::BrotliCompressBuffer(params, len, data, &compressed_len, result) == 0) { return false; @@ -60,15 +61,17 @@ bool Compress(const uint8_t* data, const size_t len, } bool Woff2Compress(const uint8_t* data, const size_t len, - uint8_t* result, uint32_t* result_len) { + uint8_t* result, uint32_t* result_len, + int quality) { return Compress(data, len, result, result_len, - brotli::BrotliParams::MODE_FONT); + brotli::BrotliParams::MODE_FONT, quality); } bool TextCompress(const uint8_t* data, const size_t len, - uint8_t* result, uint32_t* result_len) { + uint8_t* result, uint32_t* result_len, + int quality) { return Compress(data, len, result, result_len, - brotli::BrotliParams::MODE_TEXT); + brotli::BrotliParams::MODE_TEXT, quality); } int KnownTableIndex(uint32_t tag) { @@ -210,11 +213,6 @@ uint32_t CompressedBufferSize(uint32_t original_size) { return 1.2 * original_size + 10240; } -bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, - uint8_t *result, size_t *result_length) { - return ConvertTTFToWOFF2(data, length, result, result_length, ""); -} - bool TransformFontCollection(FontCollection* font_collection) { for (auto& font : font_collection->fonts) { if (!TransformGlyfAndLocaTables(&font)) { @@ -226,9 +224,16 @@ bool TransformFontCollection(FontCollection* font_collection) { return true; } +bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, + uint8_t *result, size_t *result_length) { + WOFF2Params params; + return ConvertTTFToWOFF2(data, length, result, result_length, + params); +} + bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, uint8_t *result, size_t *result_length, - const string& extended_metadata) { + const WOFF2Params& params) { FontCollection font_collection; if (!ReadFontCollection(data, length, &font_collection)) { fprintf(stderr, "Parsing of the input font failed.\n"); @@ -274,7 +279,8 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, // Compress all transformed data in one stream. if (!Woff2Compress(transform_buf.data(), total_transform_length, &compression_buf[0], - &total_compressed_length)) { + &total_compressed_length, + params.brotli_quality)) { fprintf(stderr, "Compression of combined table failed.\n"); return false; } @@ -282,14 +288,15 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, // Compress the extended metadata // TODO(user): how does this apply to collections uint32_t compressed_metadata_buf_length = - CompressedBufferSize(extended_metadata.length()); + CompressedBufferSize(params.extended_metadata.length()); std::vector compressed_metadata_buf(compressed_metadata_buf_length); - if (extended_metadata.length() > 0) { - if (!TextCompress((const uint8_t*)extended_metadata.data(), - extended_metadata.length(), + if (params.extended_metadata.length() > 0) { + if (!TextCompress((const uint8_t*)params.extended_metadata.data(), + params.extended_metadata.length(), compressed_metadata_buf.data(), - &compressed_metadata_buf_length)) { + &compressed_metadata_buf_length, + params.brotli_quality)) { fprintf(stderr, "Compression of extended metadata failed.\n"); return false; } @@ -372,7 +379,8 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, StoreU32(woff2_length - compressed_metadata_buf_length, &offset, result); // metaOffset StoreU32(compressed_metadata_buf_length, &offset, result); // metaLength - StoreU32(extended_metadata.length(), &offset, result); // metaOrigLength + StoreU32(params.extended_metadata.length(), + &offset, result); // metaOrigLength } else { StoreU32(0, &offset, result); // metaOffset StoreU32(0, &offset, result); // metaLength diff --git a/src/woff2_enc.h b/src/woff2_enc.h index d6eb4db..a1caf3f 100644 --- a/src/woff2_enc.h +++ b/src/woff2_enc.h @@ -26,6 +26,13 @@ using std::string; namespace woff2 { +struct WOFF2Params { + WOFF2Params() : extended_metadata(""), brotli_quality(11) {} + + string extended_metadata; + int brotli_quality; +}; + // Returns an upper bound on the size of the compressed file. size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length); size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length, @@ -36,10 +43,9 @@ size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length, // actual compressed size. Returns true on successful compression. bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, uint8_t *result, size_t *result_length); - bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, uint8_t *result, size_t *result_length, - const string& extended_metadata); + const WOFF2Params& params); } // namespace woff2