From 7bedd7eb6ce7ca8b85bb07484a6ffc7d785a4dc7 Mon Sep 17 00:00:00 2001 From: Matthew Denton Date: Thu, 5 Dec 2024 12:51:02 -0800 Subject: [PATCH 1/8] Spanify access to decompression input data This bumps the required C++ version to C++20, in order to use std::span. --- CMakeLists.txt | 2 +- Makefile | 2 +- src/buffer.h | 47 +++++++++++++++------------- src/glyph.cc | 2 +- src/woff2_dec.cc | 79 ++++++++++++++++++++++++------------------------ 5 files changed, 68 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ecfbb83..a287c24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_FLAG}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_FLAG}") -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) # Set search path for our private/public headers as well as Brotli headers include_directories("src" "include" diff --git a/Makefile b/Makefile index fbb945c..8b218eb 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ endif CFLAGS += $(COMMON_FLAGS) -CXXFLAGS += $(COMMON_FLAGS) -std=c++11 +CXXFLAGS += $(COMMON_FLAGS) -std=c++20 SRCDIR = src diff --git a/src/buffer.h b/src/buffer.h index 7240e51..cdfe3da 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -33,6 +33,7 @@ typedef unsigned __int64 uint64_t; #include #include #include +#include namespace woff2 { @@ -58,9 +59,9 @@ inline bool Failure(const char *f, int l, const char *fn) { class Buffer { public: Buffer(const uint8_t *data, size_t len) - : buffer_(data), - length_(len), + : buffer_(data, len), offset_(0) { } + Buffer(std::span data) : buffer_(data), offset_(0) { } bool Skip(size_t n_bytes) { return Read(NULL, n_bytes); @@ -70,19 +71,18 @@ class Buffer { if (n_bytes > 1024 * 1024 * 1024) { return FONT_COMPRESSION_FAILURE(); } - if ((offset_ + n_bytes > length_) || - (offset_ > length_ - n_bytes)) { + if (n_bytes > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } if (data) { - std::memcpy(data, buffer_ + offset_, n_bytes); + std::memcpy(data, remaining_buffer().data(), n_bytes); } offset_ += n_bytes; return true; } inline bool ReadU8(uint8_t *value) { - if (offset_ + 1 > length_) { + if (1 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } *value = buffer_[offset_]; @@ -91,10 +91,10 @@ class Buffer { } bool ReadU16(uint16_t *value) { - if (offset_ + 2 > length_) { + if (2 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - std::memcpy(value, buffer_ + offset_, sizeof(uint16_t)); + std::memcpy(value, remaining_buffer().data(), sizeof(uint16_t)); *value = ntohs(*value); offset_ += 2; return true; @@ -105,7 +105,7 @@ class Buffer { } bool ReadU24(uint32_t *value) { - if (offset_ + 3 > length_) { + if (3 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } *value = static_cast(buffer_[offset_]) << 16 | @@ -116,10 +116,10 @@ class Buffer { } bool ReadU32(uint32_t *value) { - if (offset_ + 4 > length_) { + if (4 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - std::memcpy(value, buffer_ + offset_, sizeof(uint32_t)); + std::memcpy(value, remaining_buffer().data(), sizeof(uint32_t)); *value = ntohl(*value); offset_ += 4; return true; @@ -130,32 +130,37 @@ class Buffer { } bool ReadTag(uint32_t *value) { - if (offset_ + 4 > length_) { + if (4 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - std::memcpy(value, buffer_ + offset_, sizeof(uint32_t)); + std::memcpy(value, remaining_buffer().data(), sizeof(uint32_t)); offset_ += 4; return true; } bool ReadR64(uint64_t *value) { - if (offset_ + 8 > length_) { + if (8 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - std::memcpy(value, buffer_ + offset_, sizeof(uint64_t)); + std::memcpy(value, remaining_buffer().data(), sizeof(uint64_t)); offset_ += 8; return true; } - const uint8_t *buffer() const { return buffer_; } - size_t offset() const { return offset_; } - size_t length() const { return length_; } + inline std::span remaining_buffer() const { + return buffer_.subspan(offset_); + }; + inline size_t remaining_length() const { return remaining_buffer().size(); } - void set_offset(size_t newoffset) { offset_ = newoffset; } + inline size_t offset() { return offset_; } + + void set_offset(size_t newoffset) { + offset_ = newoffset; + } private: - const uint8_t * const buffer_; - const size_t length_; + // A view of the unowned buffer. + const std::span buffer_; size_t offset_; }; diff --git a/src/glyph.cc b/src/glyph.cc index 5b49486..ca976a6 100644 --- a/src/glyph.cc +++ b/src/glyph.cc @@ -31,7 +31,7 @@ static const int32_t kFLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; bool ReadCompositeGlyphData(Buffer* buffer, Glyph* glyph) { glyph->have_instructions = false; - glyph->composite_data = buffer->buffer() + buffer->offset(); + glyph->composite_data = buffer->remaining_buffer().data(); size_t start_offset = buffer->offset(); uint16_t flags = kFLAG_MORE_COMPONENTS; while (flags & kFLAG_MORE_COMPONENTS) { diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index efb579b..b6561d9 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -78,8 +79,7 @@ struct WOFF2Header { uint32_t flavor; uint32_t header_version; uint16_t num_tables; - uint64_t compressed_offset; - uint32_t compressed_length; + std::span compressed_buf; uint32_t uncompressed_size; std::vector tables; // num_tables unique tables std::vector ttc_fonts; // metadata to help rebuild font @@ -121,12 +121,13 @@ bool _SafeIntAddition(int a, int b, int* result) { return true; } -bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, - unsigned int n_points, Point* result, size_t* in_bytes_consumed) { +bool TripletDecode(std::span flags_in, + std::span in, unsigned int n_points, + Point* result, size_t* in_bytes_consumed) { int x = 0; int y = 0; - if (PREDICT_FALSE(n_points > in_size)) { + if (PREDICT_FALSE(n_points > in.size())) { return FONT_COMPRESSION_FAILURE(); } unsigned int triplet_index = 0; @@ -145,7 +146,7 @@ bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, } else { n_data_bytes = 4; } - if (PREDICT_FALSE(triplet_index + n_data_bytes > in_size || + if (PREDICT_FALSE(triplet_index + n_data_bytes > in.size() || triplet_index + n_data_bytes < triplet_index)) { return FONT_COMPRESSION_FAILURE(); } @@ -475,7 +476,7 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, std::vector n_points_vec; std::unique_ptr points; size_t points_size = 0; - const uint8_t* bbox_bitmap = bbox_stream.buffer(); + std::span bbox_bitmap = bbox_stream.remaining_buffer(); // Safe because num_glyphs is bounded unsigned int bitmap_length = ((info->num_glyphs + 31) >> 5) << 2; if (!bbox_stream.Skip(bitmap_length)) { @@ -561,19 +562,17 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, } unsigned int flag_size = total_n_points; if (PREDICT_FALSE( - flag_size > flag_stream.length() - flag_stream.offset())) { + flag_size > flag_stream.remaining_length())) { return FONT_COMPRESSION_FAILURE(); } - const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset(); - const uint8_t* triplet_buf = glyph_stream.buffer() + - glyph_stream.offset(); - size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); + std::span flags_buf = flag_stream.remaining_buffer(); + std::span triplet_buf = glyph_stream.remaining_buffer(); size_t triplet_bytes_consumed = 0; 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, + if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, total_n_points, points.get(), &triplet_bytes_consumed))) { return FONT_COMPRESSION_FAILURE(); } @@ -796,10 +795,10 @@ bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, } bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, - const uint8_t* src_buf, size_t src_size) { + std::span src_buf) { size_t uncompressed_size = dst_size; BrotliDecoderResult result = BrotliDecoderDecompress( - src_size, src_buf, &uncompressed_size, dst_buf); + src_buf.size(), src_buf.data(), &uncompressed_size, dst_buf); if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || uncompressed_size != dst_size)) { return FONT_COMPRESSION_FAILURE(); @@ -1070,8 +1069,8 @@ bool ReconstructFont(uint8_t* transformed_buf, return true; } -bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { - Buffer file(data, length); +bool ReadWOFF2Header(std::span input_data, WOFF2Header* hdr) { + Buffer file(input_data); uint32_t signature; if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || @@ -1082,8 +1081,8 @@ bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { // TODO(user): Should call IsValidVersionTag() here. uint32_t reported_length; - if (PREDICT_FALSE( - !file.ReadU32(&reported_length) || length != reported_length)) { + if (PREDICT_FALSE(!file.ReadU32(&reported_length) || + input_data.size() != reported_length)) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!file.ReadU16(&hdr->num_tables) || !hdr->num_tables)) { @@ -1096,7 +1095,8 @@ bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { if (PREDICT_FALSE(!file.Skip(6))) { return FONT_COMPRESSION_FAILURE(); } - if (PREDICT_FALSE(!file.ReadU32(&hdr->compressed_length))) { + uint32_t compressed_length; + if (PREDICT_FALSE(!file.ReadU32(&compressed_length))) { return FONT_COMPRESSION_FAILURE(); } // We don't care about these fields of the header: @@ -1113,8 +1113,8 @@ bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { return FONT_COMPRESSION_FAILURE(); } if (meta_offset) { - if (PREDICT_FALSE( - meta_offset >= length || length - meta_offset < meta_length)) { + if (PREDICT_FALSE(meta_offset >= input_data.size() || + input_data.size() - meta_offset < meta_length)) { return FONT_COMPRESSION_FAILURE(); } } @@ -1125,8 +1125,8 @@ bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { return FONT_COMPRESSION_FAILURE(); } if (priv_offset) { - if (PREDICT_FALSE( - priv_offset >= length || length - priv_offset < priv_length)) { + if (PREDICT_FALSE(priv_offset >= input_data.size() || + input_data.size() - priv_offset < priv_length)) { return FONT_COMPRESSION_FAILURE(); } } @@ -1208,20 +1208,21 @@ bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { const uint64_t first_table_offset = ComputeOffsetToFirstTable(*hdr); - hdr->compressed_offset = file.offset(); - if (PREDICT_FALSE(hdr->compressed_offset > + uint64_t compressed_offset = file.offset(); + if (PREDICT_FALSE(compressed_offset > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } - uint64_t src_offset = Round4(hdr->compressed_offset + hdr->compressed_length); - uint64_t dst_offset = first_table_offset; - + hdr->compressed_buf = + input_data.subspan(compressed_offset, compressed_length); + uint64_t src_offset = Round4(compressed_offset + compressed_length); - if (PREDICT_FALSE(src_offset > length)) { + if (PREDICT_FALSE(src_offset > input_data.size())) { #ifdef FONT_COMPRESSION_BIN + uint64_t dst_offset = first_table_offset; fprintf(stderr, "offset fail; src_offset %" PRIu64 " length %lu " "dst_offset %" PRIu64 "\n", - src_offset, length, dst_offset); + src_offset, input_data.size(), dst_offset); #endif return FONT_COMPRESSION_FAILURE(); } @@ -1245,7 +1246,7 @@ bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { } } - if (PREDICT_FALSE(src_offset != Round4(length))) { + if (PREDICT_FALSE(src_offset != Round4(input_data.size()))) { return FONT_COMPRESSION_FAILURE(); } @@ -1253,8 +1254,7 @@ bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { } // Write everything before the actual table data -bool WriteHeaders(const uint8_t* data, size_t length, RebuildMetadata* metadata, - WOFF2Header* hdr, WOFF2Out* out) { +bool WriteHeaders(RebuildMetadata* metadata, WOFF2Header* hdr, WOFF2Out* out) { std::vector output(ComputeOffsetToFirstTable(*hdr), 0); // Re-order tables in output (OTSpec) order @@ -1355,13 +1355,14 @@ bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, bool ConvertWOFF2ToTTF(const uint8_t* data, size_t length, WOFF2Out* out) { + std::span input_data(data, length); RebuildMetadata metadata; WOFF2Header hdr; - if (!ReadWOFF2Header(data, length, &hdr)) { + if (!ReadWOFF2Header(input_data, &hdr)) { return FONT_COMPRESSION_FAILURE(); } - if (!WriteHeaders(data, length, &metadata, &hdr, out)) { + if (!WriteHeaders(&metadata, &hdr, out)) { return FONT_COMPRESSION_FAILURE(); } @@ -1373,14 +1374,12 @@ bool ConvertWOFF2ToTTF(const uint8_t* data, size_t length, return FONT_COMPRESSION_FAILURE(); } - const uint8_t* src_buf = data + hdr.compressed_offset; std::vector uncompressed_buf(hdr.uncompressed_size); if (PREDICT_FALSE(hdr.uncompressed_size < 1)) { return FONT_COMPRESSION_FAILURE(); } - if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], - hdr.uncompressed_size, src_buf, - hdr.compressed_length))) { + if (PREDICT_FALSE(!Woff2Uncompress( + &uncompressed_buf[0], hdr.uncompressed_size, hdr.compressed_buf))) { return FONT_COMPRESSION_FAILURE(); } From bee673992883d038c69e08ad4d568b04a23e2683 Mon Sep 17 00:00:00 2001 From: Matthew Denton Date: Thu, 5 Dec 2024 12:51:02 -0800 Subject: [PATCH 2/8] spanify transformed_buf in decompression --- src/store_bytes.h | 37 +++++++++++++++---- src/woff2_common.cc | 14 +++++--- src/woff2_common.h | 2 ++ src/woff2_dec.cc | 88 ++++++++++++++++++++++----------------------- 4 files changed, 86 insertions(+), 55 deletions(-) diff --git a/src/store_bytes.h b/src/store_bytes.h index 099c0f2..5bba7b0 100644 --- a/src/store_bytes.h +++ b/src/store_bytes.h @@ -14,11 +14,13 @@ #include #include +#include + #include "./port.h" namespace woff2 { -inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) { +inline size_t StoreU32(std::span dst, size_t offset, uint32_t x) { dst[offset] = x >> 24; dst[offset + 1] = x >> 16; dst[offset + 2] = x >> 8; @@ -26,28 +28,51 @@ inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) { return offset + 4; } -inline size_t Store16(uint8_t* dst, size_t offset, int x) { +inline size_t Store16(std::span dst, size_t offset, int x) { dst[offset] = x >> 8; dst[offset + 1] = x; return offset + 2; } -inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) { +inline void StoreU32(uint32_t val, size_t* offset, std::span dst) { dst[(*offset)++] = val >> 24; dst[(*offset)++] = val >> 16; dst[(*offset)++] = val >> 8; dst[(*offset)++] = val; } -inline void Store16(int val, size_t* offset, uint8_t* dst) { +inline void Store16(int val, size_t* offset, std::span dst) { dst[(*offset)++] = val >> 8; dst[(*offset)++] = val; } +inline void StoreBytes(std::span data, + size_t* offset, std::span dst) { + std::copy(data.begin(), data.end(), &dst[*offset]); + *offset += data.size_bytes(); +} + +// Same as above, but with less safety. + +inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) { + return StoreU32(std::span(dst, sizeof(uint32_t)), offset, x); +} + +inline size_t Store16(uint8_t* dst, size_t offset, int x) { + return Store16(std::span(dst, sizeof(uint16_t)), offset, x); +} + +inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) { + StoreU32(val, offset, std::span(dst, sizeof(uint32_t))); +} + +inline void Store16(int val, size_t* offset, uint8_t* dst) { + return Store16(val, offset, std::span(dst, sizeof(uint16_t))); +} + inline void StoreBytes(const uint8_t* data, size_t len, size_t* offset, uint8_t* dst) { - memcpy(&dst[*offset], data, len); - *offset += len; + StoreBytes(std::span(data, len), offset, std::span(dst, len)); } } // namespace woff2 diff --git a/src/woff2_common.cc b/src/woff2_common.cc index a24d213..23ff340 100644 --- a/src/woff2_common.cc +++ b/src/woff2_common.cc @@ -7,6 +7,7 @@ /* Helpers common across multiple parts of woff2 */ #include +#include #include "./woff2_common.h" @@ -14,19 +15,18 @@ namespace woff2 { - -uint32_t ComputeULongSum(const uint8_t* buf, size_t size) { +uint32_t ComputeULongSum(std::span buf) { uint32_t checksum = 0; - size_t aligned_size = size & ~3; + size_t aligned_size = buf.size() & ~3; for (size_t i = 0; i < aligned_size; i += 4) { checksum += (buf[i] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | buf[i + 3]; } // treat size not aligned on 4 as if it were padded to 4 with 0's - if (size != aligned_size) { + if (buf.size() != aligned_size) { uint32_t v = 0; - for (size_t i = aligned_size; i < size; ++i) { + for (size_t i = aligned_size; i < buf.size(); ++i) { v |= buf[i] << (24 - 8 * (i & 3)); } checksum += v; @@ -35,6 +35,10 @@ uint32_t ComputeULongSum(const uint8_t* buf, size_t size) { return checksum; } +uint32_t ComputeULongSum(const uint8_t* buf, size_t size) { + return ComputeULongSum(std::span(buf, size)); +} + size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts) { size_t size = 0; if (header_version == 0x00020000) { diff --git a/src/woff2_common.h b/src/woff2_common.h index 51fd4a7..f2fcaba 100644 --- a/src/woff2_common.h +++ b/src/woff2_common.h @@ -12,6 +12,7 @@ #include #include +#include #include namespace woff2 { @@ -57,6 +58,7 @@ struct Table { size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts); // Compute checksum over size bytes of buf +uint32_t ComputeULongSum(std::span buf); uint32_t ComputeULongSum(const uint8_t* buf, size_t size); } // namespace woff2 diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index b6561d9..dcc6416 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -404,14 +404,14 @@ bool StoreLoca(const std::vector& loca_values, int index_format, } // Reconstruct entire glyf table based on transformed original -bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, +bool ReconstructGlyf(std::span data, Table* glyf_table, uint32_t* glyf_checksum, Table * loca_table, uint32_t* loca_checksum, WOFF2FontInfo* info, WOFF2Out* out) { static const int kNumSubStreams = 7; - Buffer file(data, glyf_table->transform_length); + Buffer file(data.subspan(glyf_table->transform_length)); uint16_t version; - std::vector > substreams(kNumSubStreams); + std::vector> substreams(kNumSubStreams); const size_t glyf_start = out->Size(); if (PREDICT_FALSE(!file.ReadU16(&version))) { @@ -450,22 +450,22 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, if (PREDICT_FALSE(substream_size > glyf_table->transform_length - offset)) { return FONT_COMPRESSION_FAILURE(); } - substreams[i] = std::make_pair(data + offset, substream_size); + substreams[i] = data.subspan(offset, substream_size); offset += substream_size; } - Buffer n_contour_stream(substreams[0].first, substreams[0].second); - Buffer n_points_stream(substreams[1].first, substreams[1].second); - Buffer flag_stream(substreams[2].first, substreams[2].second); - Buffer glyph_stream(substreams[3].first, substreams[3].second); - Buffer composite_stream(substreams[4].first, substreams[4].second); - Buffer bbox_stream(substreams[5].first, substreams[5].second); - Buffer instruction_stream(substreams[6].first, substreams[6].second); + Buffer n_contour_stream(substreams[0]); + Buffer n_points_stream(substreams[1]); + Buffer flag_stream(substreams[2]); + Buffer glyph_stream(substreams[3]); + Buffer composite_stream(substreams[4]); + Buffer bbox_stream(substreams[5]); + Buffer instruction_stream(substreams[6]); - const uint8_t* overlap_bitmap = nullptr; + std::span overlap_bitmap; unsigned int overlap_bitmap_length = 0; if (has_overlap_bitmap) { overlap_bitmap_length = (info->num_glyphs + 7) >> 3; - overlap_bitmap = data + offset; + overlap_bitmap = data.subspan(offset); if (PREDICT_FALSE(overlap_bitmap_length > glyf_table->transform_length - offset)) { return FONT_COMPRESSION_FAILURE(); @@ -686,10 +686,9 @@ Table* FindTable(std::vector* tables, uint32_t tag) { } // Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm -bool ReadNumHMetrics(const uint8_t* data, size_t data_size, - uint16_t* num_hmetrics) { +bool ReadNumHMetrics(std::span data, uint16_t* num_hmetrics) { // Skip 34 to reach 'hhea' numberOfHMetrics - Buffer buffer(data, data_size); + Buffer buffer(data); if (PREDICT_FALSE(!buffer.Skip(34) || !buffer.ReadU16(num_hmetrics))) { return FONT_COMPRESSION_FAILURE(); } @@ -697,14 +696,13 @@ bool ReadNumHMetrics(const uint8_t* data, size_t data_size, } // http://dev.w3.org/webfonts/WOFF2/spec/Overview.html#hmtx_table_format -bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, - size_t transformed_size, +bool ReconstructTransformedHmtx(std::span transformed_buf, uint16_t num_glyphs, uint16_t num_hmetrics, const std::vector& x_mins, uint32_t* checksum, WOFF2Out* out) { - Buffer hmtx_buff_in(transformed_buf, transformed_size); + Buffer hmtx_buff_in(transformed_buf); uint8_t hmtx_flags; if (PREDICT_FALSE(!hmtx_buff_in.ReadU8(&hmtx_flags))) { @@ -777,7 +775,7 @@ bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, // bake me a shiny new hmtx table uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics; std::vector hmtx_table(hmtx_output_size); - uint8_t* dst = &hmtx_table[0]; + std::span dst(hmtx_table); size_t dst_offset = 0; for (uint32_t i = 0; i < num_glyphs; i++) { if (i < num_hmetrics) { @@ -794,13 +792,13 @@ bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, return true; } -bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, - std::span src_buf) { - size_t uncompressed_size = dst_size; +bool Woff2Uncompress(std::span dst_buf, + std::span src_buf) { + size_t uncompressed_size = dst_buf.size_bytes(); BrotliDecoderResult result = BrotliDecoderDecompress( - src_buf.size(), src_buf.data(), &uncompressed_size, dst_buf); + src_buf.size(), src_buf.data(), &uncompressed_size, dst_buf.data()); if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || - uncompressed_size != dst_size)) { + uncompressed_size != dst_buf.size_bytes())) { return FONT_COMPRESSION_FAILURE(); } return true; @@ -919,8 +917,7 @@ std::vector Tables(WOFF2Header* hdr, size_t font_index) { // Offset tables assumed to have been written in with 0's initially. // WOFF2Header isn't const so we can use [] instead of at() (which upsets FF) -bool ReconstructFont(uint8_t* transformed_buf, - const uint32_t transformed_buf_size, +bool ReconstructFont(std::span transformed_buf, RebuildMetadata* metadata, WOFF2Header* hdr, size_t font_index, @@ -970,13 +967,17 @@ bool ReconstructFont(uint8_t* transformed_buf, // TODO(user) a collection with optimized hmtx that reused glyf/loca // would fail. We don't optimize hmtx for collections yet. if (PREDICT_FALSE(static_cast(table.src_offset) + table.src_length - > transformed_buf_size)) { + > transformed_buf.size())) { return FONT_COMPRESSION_FAILURE(); } + std::span transformed_table = + transformed_buf.subspan(table.src_offset, table.src_length); + if (table.tag == kHheaTableTag) { - if (!ReadNumHMetrics(transformed_buf + table.src_offset, - table.src_length, &info->num_hmetrics)) { + if (!ReadNumHMetrics( + transformed_table, + &info->num_hmetrics)) { return FONT_COMPRESSION_FAILURE(); } } @@ -989,13 +990,12 @@ bool ReconstructFont(uint8_t* transformed_buf, return FONT_COMPRESSION_FAILURE(); } // checkSumAdjustment = 0 - StoreU32(transformed_buf + table.src_offset, 8, 0); + StoreU32(transformed_table, 8, 0); } table.dst_offset = dest_offset; - checksum = ComputeULongSum(transformed_buf + table.src_offset, - table.src_length); - if (PREDICT_FALSE(!out->Write(transformed_buf + table.src_offset, - table.src_length))) { + checksum = ComputeULongSum(transformed_table); + if (PREDICT_FALSE(!out->Write(transformed_table.data(), + transformed_table.size_bytes()))) { return FONT_COMPRESSION_FAILURE(); } } else { @@ -1003,8 +1003,9 @@ bool ReconstructFont(uint8_t* transformed_buf, table.dst_offset = dest_offset; Table* loca_table = FindTable(&tables, kLocaTableTag); - if (PREDICT_FALSE(!ReconstructGlyf(transformed_buf + table.src_offset, - &table, &checksum, loca_table, &loca_checksum, info, out))) { + if (PREDICT_FALSE(!ReconstructGlyf(transformed_table, &table, + &checksum, loca_table, + &loca_checksum, info, out))) { return FONT_COMPRESSION_FAILURE(); } } else if (table.tag == kLocaTableTag) { @@ -1014,9 +1015,8 @@ bool ReconstructFont(uint8_t* transformed_buf, table.dst_offset = dest_offset; // Tables are sorted so all the info we need has been gathered. if (PREDICT_FALSE(!ReconstructTransformedHmtx( - transformed_buf + table.src_offset, table.src_length, - info->num_glyphs, info->num_hmetrics, info->x_mins, &checksum, - out))) { + transformed_table, info->num_glyphs, info->num_hmetrics, + info->x_mins, &checksum, out))) { return FONT_COMPRESSION_FAILURE(); } } else { @@ -1375,17 +1375,17 @@ bool ConvertWOFF2ToTTF(const uint8_t* data, size_t length, } std::vector uncompressed_buf(hdr.uncompressed_size); + std::span uncompressed_buf_view(uncompressed_buf); if (PREDICT_FALSE(hdr.uncompressed_size < 1)) { return FONT_COMPRESSION_FAILURE(); } - if (PREDICT_FALSE(!Woff2Uncompress( - &uncompressed_buf[0], hdr.uncompressed_size, hdr.compressed_buf))) { + if (PREDICT_FALSE( + !Woff2Uncompress(uncompressed_buf_view, hdr.compressed_buf))) { return FONT_COMPRESSION_FAILURE(); } for (size_t i = 0; i < metadata.font_infos.size(); i++) { - if (PREDICT_FALSE(!ReconstructFont(&uncompressed_buf[0], - hdr.uncompressed_size, + if (PREDICT_FALSE(!ReconstructFont(uncompressed_buf_view, &metadata, &hdr, i, out))) { return FONT_COMPRESSION_FAILURE(); } From c8eebb573af2d99225348cc853f98f4623876a96 Mon Sep 17 00:00:00 2001 From: Matthew Denton Date: Thu, 5 Dec 2024 18:12:22 -0800 Subject: [PATCH 3/8] spanify WriteHeaders StoreU32 calls --- src/woff2_dec.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index dcc6416..76fb4f5 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -863,8 +863,8 @@ bool ReadTableDirectory(Buffer* file, std::vector
* tables, } // Writes a single Offset Table entry -size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, - uint16_t num_tables) { +size_t StoreOffsetTable(std::span result, size_t offset, + uint32_t flavor, uint16_t num_tables) { offset = StoreU32(result, offset, flavor); // sfnt version offset = Store16(result, offset, num_tables); // num_tables unsigned max_pow2 = 0; @@ -879,7 +879,8 @@ size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, return offset; } -size_t StoreTableEntry(uint8_t* result, uint32_t offset, uint32_t tag) { +size_t StoreTableEntry(std::span result, uint32_t offset, + uint32_t tag) { offset = StoreU32(result, offset, tag); offset = StoreU32(result, offset, 0); offset = StoreU32(result, offset, 0); @@ -1277,7 +1278,7 @@ bool WriteHeaders(RebuildMetadata* metadata, WOFF2Header* hdr, WOFF2Out* out) { } // Start building the font - uint8_t* result = &output[0]; + std::span result(output); size_t offset = 0; if (hdr->header_version) { // TTC header From e359b5dc7e9c1c9ffe6e0bc6f7b6cc8f586fc910 Mon Sep 17 00:00:00 2001 From: Matthew Denton Date: Tue, 10 Dec 2024 12:25:43 -0800 Subject: [PATCH 4/8] Spanify some remaining StoreU32 and Store16 in decompression --- src/woff2_dec.cc | 68 +++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index 76fb4f5..b235f18 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -193,7 +193,7 @@ bool TripletDecode(std::span flags_in, // beginning of a simple glyph. Returns true on success. bool StorePoints(unsigned int n_points, const Point* points, unsigned int n_contours, unsigned int instruction_length, - bool has_overlap_bit, uint8_t* dst, size_t dst_size, + bool has_overlap_bit, std::span dst, size_t* glyph_size) { // I believe that n_contours < 65536, in which case this is safe. However, a // comment and/or an assert would be good. @@ -237,12 +237,12 @@ bool StorePoints(unsigned int n_points, const Point* points, repeat_count++; } else { if (repeat_count != 0) { - if (PREDICT_FALSE(flag_offset >= dst_size)) { + if (PREDICT_FALSE(flag_offset >= dst.size_bytes())) { return FONT_COMPRESSION_FAILURE(); } dst[flag_offset++] = repeat_count; } - if (PREDICT_FALSE(flag_offset >= dst_size)) { + if (PREDICT_FALSE(flag_offset >= dst.size_bytes())) { return FONT_COMPRESSION_FAILURE(); } dst[flag_offset++] = flag; @@ -254,7 +254,7 @@ bool StorePoints(unsigned int n_points, const Point* points, } if (repeat_count != 0) { - if (PREDICT_FALSE(flag_offset >= dst_size)) { + if (PREDICT_FALSE(flag_offset >= dst.size_bytes())) { return FONT_COMPRESSION_FAILURE(); } dst[flag_offset++] = repeat_count; @@ -262,7 +262,7 @@ bool StorePoints(unsigned int n_points, const Point* points, unsigned int xy_bytes = x_bytes + y_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_bytes())) { return FONT_COMPRESSION_FAILURE(); } @@ -298,7 +298,8 @@ bool StorePoints(unsigned int n_points, const Point* 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. // dst should point to the beginning of a 'glyf' record. -void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { +void ComputeBbox(unsigned int n_points, const Point* points, + std::span dst) { int x_min = 0; int y_min = 0; int x_max = 0; @@ -325,7 +326,6 @@ void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { offset = Store16(dst, offset, y_max); } - bool SizeOfComposite(Buffer composite_stream, size_t* size, bool* have_instructions) { size_t start_offset = composite_stream.offset(); @@ -386,18 +386,19 @@ bool StoreLoca(const std::vector& loca_values, int index_format, return FONT_COMPRESSION_FAILURE(); } std::vector loca_content(loca_size * offset_size); - uint8_t* dst = &loca_content[0]; + std::span loca_content_view(loca_content); size_t offset = 0; for (size_t i = 0; i < loca_values.size(); ++i) { uint32_t value = loca_values[i]; if (index_format) { - offset = StoreU32(dst, offset, value); + offset = StoreU32(loca_content_view, offset, value); } else { - offset = Store16(dst, offset, value >> 1); + offset = Store16(loca_content_view, offset, value >> 1); } } - *checksum = ComputeULongSum(&loca_content[0], loca_content.size()); - if (PREDICT_FALSE(!out->Write(&loca_content[0], loca_content.size()))) { + *checksum = ComputeULongSum(loca_content_view); + if (PREDICT_FALSE( + !out->Write(loca_content_view.data(), loca_content_view.size()))) { return FONT_COMPRESSION_FAILURE(); } return true; @@ -484,8 +485,8 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, } // Temp buffer for glyph's. - size_t glyph_buf_size = kDefaultGlyphBuf; - std::unique_ptr glyph_buf(new uint8_t[glyph_buf_size]); + std::unique_ptr glyph_buf(new uint8_t[kDefaultGlyphBuf]); + std::span glyph_buf_view(glyph_buf.get(), kDefaultGlyphBuf); info->x_mins.resize(info->num_glyphs); for (unsigned int i = 0; i < info->num_glyphs; ++i) { @@ -520,12 +521,12 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, } size_t size_needed = 12 + composite_size + instruction_size; - if (PREDICT_FALSE(glyph_buf_size < size_needed)) { + if (PREDICT_FALSE(glyph_buf_view.size() < size_needed)) { glyph_buf.reset(new uint8_t[size_needed]); - glyph_buf_size = size_needed; + glyph_buf_view = std::span(glyph_buf.get(), size_needed); } - glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); + glyph_size = Store16(glyph_buf_view, glyph_size, n_contours); if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { return FONT_COMPRESSION_FAILURE(); } @@ -537,7 +538,7 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, } glyph_size += composite_size; if (have_instructions) { - glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); + glyph_size = Store16(glyph_buf_view, glyph_size, instruction_size); if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, instruction_size))) { return FONT_COMPRESSION_FAILURE(); @@ -593,18 +594,18 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, } size_t size_needed = 12 + 2 * n_contours + 5 * total_n_points + instruction_size; - if (PREDICT_FALSE(glyph_buf_size < size_needed)) { + if (PREDICT_FALSE(glyph_buf_view.size() < size_needed)) { glyph_buf.reset(new uint8_t[size_needed]); - glyph_buf_size = size_needed; + glyph_buf_view = std::span(glyph_buf.get(), size_needed); } - glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); + glyph_size = Store16(glyph_buf_view, glyph_size, n_contours); if (have_bbox) { if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { return FONT_COMPRESSION_FAILURE(); } } else { - ComputeBbox(total_n_points, points.get(), glyph_buf.get()); + ComputeBbox(total_n_points, points.get(), glyph_buf_view); } glyph_size = kEndPtsOfContoursOffset; int end_point = -1; @@ -613,10 +614,10 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, if (PREDICT_FALSE(end_point >= 65536)) { return FONT_COMPRESSION_FAILURE(); } - glyph_size = Store16(glyph_buf.get(), glyph_size, end_point); + glyph_size = Store16(glyph_buf_view, glyph_size, end_point); } - glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); + glyph_size = Store16(glyph_buf_view, glyph_size, instruction_size); if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, instruction_size))) { return FONT_COMPRESSION_FAILURE(); @@ -628,7 +629,7 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, if (PREDICT_FALSE(!StorePoints( total_n_points, points.get(), n_contours, instruction_size, - has_overlap_bit, glyph_buf.get(), glyph_buf_size, &glyph_size))) { + has_overlap_bit, glyph_buf_view, &glyph_size))) { return FONT_COMPRESSION_FAILURE(); } } else { @@ -655,7 +656,7 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, // We may need x_min to reconstruct 'hmtx' if (n_contours > 0) { - Buffer x_min_buf(glyph_buf.get() + 2, 2); + Buffer x_min_buf(glyph_buf_view.subspan<2, 2>()); if (PREDICT_FALSE(!x_min_buf.ReadS16(&info->x_mins[i]))) { return FONT_COMPRESSION_FAILURE(); } @@ -924,7 +925,7 @@ bool ReconstructFont(std::span transformed_buf, size_t font_index, WOFF2Out* out) { size_t dest_offset = out->Size(); - uint8_t table_entry[12]; + std::array table_entry; WOFF2FontInfo* info = &metadata->font_infos[font_index]; std::vector tables = Tables(hdr, font_index); @@ -1034,13 +1035,13 @@ bool ReconstructFont(std::span transformed_buf, StoreU32(table_entry, 0, checksum); StoreU32(table_entry, 4, table.dst_offset); StoreU32(table_entry, 8, table.dst_length); - if (PREDICT_FALSE(!out->Write(table_entry, - info->table_entry_by_tag[table.tag] + 4, 12))) { + if (PREDICT_FALSE(!out->Write(table_entry.data(), + info->table_entry_by_tag[table.tag] + 4, table_entry.size()))) { return FONT_COMPRESSION_FAILURE(); } // We replaced 0's. Update overall checksum. - font_checksum += ComputeULongSum(table_entry, 12); + font_checksum += ComputeULongSum(table_entry); if (PREDICT_FALSE(!Pad4(out))) { return FONT_COMPRESSION_FAILURE(); @@ -1059,10 +1060,11 @@ bool ReconstructFont(std::span transformed_buf, if (PREDICT_FALSE(head_table->dst_length < 12)) { return FONT_COMPRESSION_FAILURE(); } - uint8_t checksum_adjustment[4]; + std::array checksum_adjustment; StoreU32(checksum_adjustment, 0, 0xB1B0AFBA - font_checksum); - if (PREDICT_FALSE(!out->Write(checksum_adjustment, - head_table->dst_offset + 8, 4))) { + if (PREDICT_FALSE(!out->Write(checksum_adjustment.data(), + head_table->dst_offset + 8, + checksum_adjustment.size()))) { return FONT_COMPRESSION_FAILURE(); } } From e08467a919347fb19f8b4b6ae18d5f371e502dba Mon Sep 17 00:00:00 2001 From: Matthew Denton Date: Tue, 10 Dec 2024 13:09:53 -0800 Subject: [PATCH 5/8] spanify the buffer READ*() functions --- src/buffer.h | 34 ++++++++++++++++++++++++---------- src/woff2_dec.cc | 18 ++++++++++-------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/buffer.h b/src/buffer.h index cdfe3da..54030fd 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -64,18 +64,20 @@ class Buffer { Buffer(std::span data) : buffer_(data), offset_(0) { } bool Skip(size_t n_bytes) { - return Read(NULL, n_bytes); + return Read(std::span(), n_bytes); } - bool Read(uint8_t *data, size_t n_bytes) { + bool Read(std::span data, size_t n_bytes) { if (n_bytes > 1024 * 1024 * 1024) { return FONT_COMPRESSION_FAILURE(); } if (n_bytes > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - if (data) { - std::memcpy(data, remaining_buffer().data(), n_bytes); + if (data.size() != 0) { + std::span buffer_view = + remaining_buffer().subspan(0, n_bytes); + std::copy(buffer_view.begin(), buffer_view.end(), data.begin()); } offset_ += n_bytes; return true; @@ -94,8 +96,8 @@ class Buffer { if (2 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - std::memcpy(value, remaining_buffer().data(), sizeof(uint16_t)); - *value = ntohs(*value); + *value = static_cast(buffer_[offset_]) << 8 | + static_cast(buffer_[offset_ + 1]); offset_ += 2; return true; } @@ -119,8 +121,10 @@ class Buffer { if (4 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - std::memcpy(value, remaining_buffer().data(), sizeof(uint32_t)); - *value = ntohl(*value); + *value = static_cast(buffer_[offset_]) << 24 | + static_cast(buffer_[offset_ + 1]) << 16 | + static_cast(buffer_[offset_ + 2]) << 8 | + static_cast(buffer_[offset_ + 3]); offset_ += 4; return true; } @@ -133,7 +137,10 @@ class Buffer { if (4 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - std::memcpy(value, remaining_buffer().data(), sizeof(uint32_t)); + *value = static_cast(buffer_[offset_]) | + static_cast(buffer_[offset_ + 1]) << 8 | + static_cast(buffer_[offset_ + 2]) << 16 | + static_cast(buffer_[offset_ + 3]) << 24; offset_ += 4; return true; } @@ -142,7 +149,14 @@ class Buffer { if (8 > remaining_length()) { return FONT_COMPRESSION_FAILURE(); } - std::memcpy(value, remaining_buffer().data(), sizeof(uint64_t)); + *value = static_cast(buffer_[offset_]) << 56 | + static_cast(buffer_[offset_ + 1]) << 48 | + static_cast(buffer_[offset_ + 2]) << 40 | + static_cast(buffer_[offset_ + 3]) << 32 | + static_cast(buffer_[offset_ + 4]) << 24 | + static_cast(buffer_[offset_ + 5]) << 16 | + static_cast(buffer_[offset_ + 6]) << 8 | + static_cast(buffer_[offset_ + 7]); offset_ += 8; return true; } diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index b235f18..fdb7799 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -527,20 +527,21 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, } glyph_size = Store16(glyph_buf_view, glyph_size, n_contours); - if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { + if (PREDICT_FALSE( + !bbox_stream.Read(glyph_buf_view.subspan(glyph_size), 8))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += 8; - if (PREDICT_FALSE(!composite_stream.Read(glyph_buf.get() + glyph_size, - composite_size))) { + if (PREDICT_FALSE(!composite_stream.Read( + glyph_buf_view.subspan(glyph_size), composite_size))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += composite_size; if (have_instructions) { glyph_size = Store16(glyph_buf_view, glyph_size, instruction_size); - if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, - instruction_size))) { + if (PREDICT_FALSE(!instruction_stream.Read( + glyph_buf_view.subspan(glyph_size), instruction_size))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += instruction_size; @@ -601,7 +602,8 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, glyph_size = Store16(glyph_buf_view, glyph_size, n_contours); if (have_bbox) { - if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { + if (PREDICT_FALSE( + !bbox_stream.Read(glyph_buf_view.subspan(glyph_size), 8))) { return FONT_COMPRESSION_FAILURE(); } } else { @@ -618,8 +620,8 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, } glyph_size = Store16(glyph_buf_view, glyph_size, instruction_size); - if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, - instruction_size))) { + if (PREDICT_FALSE(!instruction_stream.Read( + glyph_buf_view.subspan(glyph_size), instruction_size))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += instruction_size; From 04f82797c0f7ddb16d21c3b228bc6374d58f5f80 Mon Sep 17 00:00:00 2001 From: Matthew Denton Date: Tue, 10 Dec 2024 13:42:27 -0800 Subject: [PATCH 6/8] Spanify Points[] array in decompression --- src/woff2_dec.cc | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index fdb7799..060bd0e 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -122,17 +122,17 @@ bool _SafeIntAddition(int a, int b, int* result) { } bool TripletDecode(std::span flags_in, - std::span in, unsigned int n_points, - Point* result, size_t* in_bytes_consumed) { + std::span in, std::span results, + size_t* in_bytes_consumed) { int x = 0; int y = 0; - if (PREDICT_FALSE(n_points > in.size())) { + if (PREDICT_FALSE(results.size() > in.size())) { return FONT_COMPRESSION_FAILURE(); } unsigned int triplet_index = 0; - for (unsigned int i = 0; i < n_points; ++i) { + for (unsigned int i = 0; i < results.size(); ++i) { uint8_t flag = flags_in[i]; bool on_curve = !(flag >> 7); flag &= 0x7f; @@ -183,7 +183,7 @@ bool TripletDecode(std::span flags_in, if (!_SafeIntAddition(y, dy, &y)) { return false; } - *result++ = {x, y, on_curve}; + results[i] = {x, y, on_curve}; } *in_bytes_consumed = triplet_index; return true; @@ -191,10 +191,9 @@ bool TripletDecode(std::span flags_in, // This function stores just the point data. On entry, dst points to the // beginning of a simple glyph. Returns true on success. -bool StorePoints(unsigned int n_points, const Point* points, - unsigned int n_contours, unsigned int instruction_length, - bool has_overlap_bit, std::span dst, - size_t* glyph_size) { +bool StorePoints(std::span points, unsigned int n_contours, + unsigned int instruction_length, bool has_overlap_bit, + std::span dst, size_t* glyph_size) { // I believe that n_contours < 65536, in which case this is safe. However, a // comment and/or an assert would be good. unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 + @@ -206,7 +205,7 @@ bool StorePoints(unsigned int n_points, const Point* points, unsigned int x_bytes = 0; unsigned int y_bytes = 0; - for (unsigned int i = 0; i < n_points; ++i) { + for (unsigned int i = 0; i < points.size(); ++i) { const Point& point = points[i]; int flag = point.on_curve ? kGlyfOnCurve : 0; if (has_overlap_bit && i == 0) { @@ -270,7 +269,7 @@ bool StorePoints(unsigned int n_points, const Point* points, int y_offset = flag_offset + x_bytes; last_x = 0; last_y = 0; - for (unsigned int i = 0; i < n_points; ++i) { + for (unsigned int i = 0; i < points.size(); ++i) { int dx = points[i].x - last_x; if (dx == 0) { // pass @@ -298,20 +297,19 @@ bool StorePoints(unsigned int n_points, const Point* 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. // dst should point to the beginning of a 'glyf' record. -void ComputeBbox(unsigned int n_points, const Point* points, - std::span dst) { +void ComputeBbox(std::span points, std::span dst) { int x_min = 0; int y_min = 0; int x_max = 0; int y_max = 0; - if (n_points > 0) { + if (points.size() > 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) { + for (unsigned int i = 1; i < points.size(); ++i) { int x = points[i].x; int y = points[i].y; x_min = std::min(x, x_min); @@ -476,7 +474,7 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, std::vector loca_values(info->num_glyphs + 1); std::vector n_points_vec; std::unique_ptr points; - size_t points_size = 0; + std::span points_view; std::span bbox_bitmap = bbox_stream.remaining_buffer(); // Safe because num_glyphs is bounded unsigned int bitmap_length = ((info->num_glyphs + 31) >> 5) << 2; @@ -570,12 +568,12 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, std::span flags_buf = flag_stream.remaining_buffer(); std::span triplet_buf = glyph_stream.remaining_buffer(); size_t triplet_bytes_consumed = 0; - if (points_size < total_n_points) { - points_size = total_n_points; - points.reset(new Point[points_size]); + if (points_view.size() < total_n_points) { + points.reset(new Point[total_n_points]); + points_view = std::span(points.get(), total_n_points); } if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, - total_n_points, points.get(), &triplet_bytes_consumed))) { + points_view, &triplet_bytes_consumed))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { @@ -607,7 +605,7 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, return FONT_COMPRESSION_FAILURE(); } } else { - ComputeBbox(total_n_points, points.get(), glyph_buf_view); + ComputeBbox(points_view, glyph_buf_view); } glyph_size = kEndPtsOfContoursOffset; int end_point = -1; @@ -629,9 +627,9 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, bool has_overlap_bit = has_overlap_bitmap && overlap_bitmap[i >> 3] & (0x80 >> (i & 7)); - if (PREDICT_FALSE(!StorePoints( - total_n_points, points.get(), n_contours, instruction_size, - has_overlap_bit, glyph_buf_view, &glyph_size))) { + if (PREDICT_FALSE(!StorePoints(points_view, n_contours, instruction_size, + has_overlap_bit, glyph_buf_view, + &glyph_size))) { return FONT_COMPRESSION_FAILURE(); } } else { From 9e21ce02424e1c9ebaabc46c74101fde995daba8 Mon Sep 17 00:00:00 2001 From: Matthew Denton Date: Tue, 10 Dec 2024 15:49:30 -0800 Subject: [PATCH 7/8] Spanify all users of ComputeULongSum and delete raw ptr version --- src/normalize.cc | 2 +- src/woff2_common.cc | 4 ---- src/woff2_common.h | 3 +-- src/woff2_dec.cc | 26 +++++++++++++------------- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/normalize.cc b/src/normalize.cc index 6685e08..183468b 100644 --- a/src/normalize.cc +++ b/src/normalize.cc @@ -219,7 +219,7 @@ bool FixChecksums(Font* font) { if (table->IsReused()) { table = table->reuse_of; } - table->checksum = ComputeULongSum(table->data, table->length); + table->checksum = ComputeULongSum(std::span(table->data, table->length)); file_checksum += table->checksum; if (table->tag == kHeadTableTag) { diff --git a/src/woff2_common.cc b/src/woff2_common.cc index 23ff340..c2a799e 100644 --- a/src/woff2_common.cc +++ b/src/woff2_common.cc @@ -35,10 +35,6 @@ uint32_t ComputeULongSum(std::span buf) { return checksum; } -uint32_t ComputeULongSum(const uint8_t* buf, size_t size) { - return ComputeULongSum(std::span(buf, size)); -} - size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts) { size_t size = 0; if (header_version == 0x00020000) { diff --git a/src/woff2_common.h b/src/woff2_common.h index f2fcaba..c1e0185 100644 --- a/src/woff2_common.h +++ b/src/woff2_common.h @@ -57,9 +57,8 @@ struct Table { // True Type Collections size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts); -// Compute checksum over size bytes of buf +// Compute checksum over buf uint32_t ComputeULongSum(std::span buf); -uint32_t ComputeULongSum(const uint8_t* buf, size_t size); } // namespace woff2 diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index 060bd0e..174d8ee 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -652,7 +652,7 @@ bool ReconstructGlyf(std::span data, Table* glyf_table, return FONT_COMPRESSION_FAILURE(); } - *glyf_checksum += ComputeULongSum(glyph_buf.get(), glyph_size); + *glyf_checksum += ComputeULongSum(glyph_buf_view.subspan(0, glyph_size)); // We may need x_min to reconstruct 'hmtx' if (n_contours > 0) { @@ -774,19 +774,19 @@ bool ReconstructTransformedHmtx(std::span transformed_buf, } // bake me a shiny new hmtx table - uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics; - std::vector hmtx_table(hmtx_output_size); - std::span dst(hmtx_table); - size_t dst_offset = 0; + std::vector hmtx_table(2 * num_glyphs + 2 * num_hmetrics); + std::span hmtx_table_view(hmtx_table); + size_t hmtx_table_view_offset = 0; for (uint32_t i = 0; i < num_glyphs; i++) { if (i < num_hmetrics) { - Store16(advance_widths[i], &dst_offset, dst); + Store16(advance_widths[i], &hmtx_table_view_offset, hmtx_table_view); } - Store16(lsbs[i], &dst_offset, dst); + Store16(lsbs[i], &hmtx_table_view_offset, hmtx_table_view); } - *checksum = ComputeULongSum(&hmtx_table[0], hmtx_output_size); - if (PREDICT_FALSE(!out->Write(&hmtx_table[0], hmtx_output_size))) { + *checksum = ComputeULongSum(hmtx_table_view); + if (PREDICT_FALSE( + !out->Write(hmtx_table_view.data(), hmtx_table_view.size()))) { return FONT_COMPRESSION_FAILURE(); } @@ -1280,7 +1280,7 @@ bool WriteHeaders(RebuildMetadata* metadata, WOFF2Header* hdr, WOFF2Out* out) { } // Start building the font - std::span result(output); + const std::span result(output); size_t offset = 0; if (hdr->header_version) { // TTC header @@ -1318,8 +1318,8 @@ bool WriteHeaders(RebuildMetadata* metadata, WOFF2Header* hdr, WOFF2Out* out) { offset = StoreTableEntry(result, offset, tag); } - ttc_font.header_checksum = ComputeULongSum(&output[ttc_font.dst_offset], - offset - ttc_font.dst_offset); + ttc_font.header_checksum = ComputeULongSum( + result.subspan(ttc_font.dst_offset, offset - ttc_font.dst_offset)); } } else { metadata->font_infos.resize(1); @@ -1333,7 +1333,7 @@ bool WriteHeaders(RebuildMetadata* metadata, WOFF2Header* hdr, WOFF2Out* out) { if (PREDICT_FALSE(!out->Write(&output[0], output.size()))) { return FONT_COMPRESSION_FAILURE(); } - metadata->header_checksum = ComputeULongSum(&output[0], output.size()); + metadata->header_checksum = ComputeULongSum(output); return true; } From 1c208bc1e3802089cd2bb52a928fc4bace3345e0 Mon Sep 17 00:00:00 2001 From: Matthew Denton Date: Tue, 10 Dec 2024 17:32:17 -0800 Subject: [PATCH 8/8] Add enc_dec_fuzzer This fuzzer takes input, compresses it, decompressed, and compresses it again, and checks that the two compressed versions are equal. This is a simple correctness fuzzer. Note that we can't just compare the decompressed version to the original input because decompression might produce a slightly different TTF file to the input, even though they are functionally the exact same thing. --- CMakeLists.txt | 3 +++ Makefile | 2 +- src/enc_dec_fuzzer.cc | 52 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/enc_dec_fuzzer.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a287c24..351f4e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,9 @@ add_library(convert_woff2ttf_fuzzer STATIC src/convert_woff2ttf_fuzzer.cc) target_link_libraries(convert_woff2ttf_fuzzer woff2dec) add_library(convert_woff2ttf_fuzzer_new_entry STATIC src/convert_woff2ttf_fuzzer_new_entry.cc) target_link_libraries(convert_woff2ttf_fuzzer_new_entry woff2dec) +add_library(enc_dec_fuzzer STATIC src/enc_dec_fuzzer.cc) +target_link_libraries(enc_dec_fuzzer woff2dec woff2enc) +# clang++ -fsanitize=fuzzer -o enc_dec_fuzzer libwoff2common.so libwoff2dec.so libwoff2enc.so libenc_dec_fuzzer.a # PC files include(CMakeParseArguments) diff --git a/Makefile b/Makefile index 8b218eb..773c516 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ COMMONOBJ = $(BROTLIOBJ)/common/*.o OBJS = $(patsubst %, $(SRCDIR)/%, $(OUROBJ)) EXECUTABLES=woff2_compress woff2_decompress woff2_info EXE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(EXECUTABLES)) -ARCHIVES=convert_woff2ttf_fuzzer convert_woff2ttf_fuzzer_new_entry +ARCHIVES=convert_woff2ttf_fuzzer convert_woff2ttf_fuzzer_new_entry enc_dec_fuzzer ARCHIVE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(ARCHIVES)) ifeq (,$(wildcard $(BROTLI)/*)) diff --git a/src/enc_dec_fuzzer.cc b/src/enc_dec_fuzzer.cc new file mode 100644 index 0000000..038ebd3 --- /dev/null +++ b/src/enc_dec_fuzzer.cc @@ -0,0 +1,52 @@ +#include +#include + +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t data_size) { + size_t encoded_size = woff2::MaxWOFF2CompressedSize(data, data_size); + std::string encoded(encoded_size, 0); + uint8_t* encoded_data = reinterpret_cast(&encoded[0]); + + woff2::WOFF2Params params; + if (!woff2::ConvertTTFToWOFF2(data, data_size, encoded_data, &encoded_size, + params)) { + // Do not record this in the corpus + return -1; + } + encoded.resize(encoded_size); + + // Decode using newer entry pattern. + // Same pattern as woff2_decompress. + std::string decoded_output( + std::min(woff2::ComputeWOFF2FinalSize(encoded_data, encoded.size()), + woff2::kDefaultMaxSize), + 0); + woff2::WOFF2StringOut out(&decoded_output); + woff2::ConvertWOFF2ToTTF(encoded_data, encoded.size(), &out); + + // Convert back to encoded version. + size_t re_encoded_size = encoded_size; + std::string re_encoded(re_encoded_size, 0); + uint8_t* re_encoded_data = reinterpret_cast(&re_encoded[0]); + if (!woff2::ConvertTTFToWOFF2( + reinterpret_cast(decoded_output.data()), + decoded_output.size(), re_encoded_data, &re_encoded_size, params)) { + fprintf(stderr, "Compression failed.\n"); + return -1; + } + re_encoded.resize(re_encoded_size); + + // Compressed data == compressed/decompressed/compressed data. + // Note that compressed/decompressed may not be the same as the original data + // provided by libfuzzer because our decompression may output a slightly + // different but functionally equivalent TTF file. + if (encoded_size != re_encoded_size) { + __builtin_trap(); + } + if (memcmp(encoded.data(), re_encoded.data(), encoded_size) != 0) { + __builtin_trap(); + } + + return 0; +}