| /* |
| * Copyright 2011 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <vector> |
| #include <algorithm> |
| |
| #include "gtest/gtest.h" |
| #include "sfntly/port/type.h" |
| #include "sfntly/data/writable_font_data.h" |
| #include "sfntly/data/memory_byte_array.h" |
| |
| namespace sfntly { |
| |
| const int32_t BYTE_ARRAY_SIZES[] = |
| {1, 7, 127, 128, 129, 255, 256, 257, 666, 1023, 0x10000}; |
| |
| // array data for searching |
| const int32_t LOWER_BYTE_ARRAY_FOR_SEARCHING[] = {2, 4, 7, 13, 127}; |
| const int32_t UPPER_BYTE_ARRAY_FOR_SEARCHING[] = {2, 5, 12, 16, 256}; |
| const int32_t kLowerByteArrayForSearchingLength = 5; |
| const int32_t kUpperByteArrayForSearchingLength = 5; |
| |
| // search test result pairs - number to search for; index found at |
| const int32_t SEARCH_TEST_PAIRS[][2] = { |
| {0, -1}, {1, -1}, {2, 0}, {3, -1}, {4, 1}, {5, 1}, {6, -1}, {12, 2}, |
| {13, 3}, {17, -1}, {126, -1}, {127, 4}, {256, 4}, {257, -1}, {0x1000, -1} |
| }; |
| const int32_t kSearchTestPairsLength = 15; |
| |
| // offset and start index data for searching data |
| // array data size, lower_start_index, lower_offset, upper_start_index, |
| // upper_offset |
| const int32_t SEARCH_TEST_OFFSETS[][5] = { |
| // lower[], upper[] |
| { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength) |
| * DataSize::kUSHORT, |
| 0, |
| DataSize::kUSHORT, |
| kLowerByteArrayForSearchingLength * DataSize::kUSHORT, |
| DataSize::kUSHORT }, |
| |
| // {lower, upper} [] |
| { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength) |
| * DataSize::kUSHORT, |
| 0, |
| 2 * DataSize::kUSHORT, |
| DataSize::kUSHORT, |
| 2 * DataSize::kUSHORT }, |
| |
| // upper[], lower[] |
| { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength) |
| * DataSize::kUSHORT, |
| kLowerByteArrayForSearchingLength * DataSize::kUSHORT, |
| DataSize::kUSHORT, |
| 0, |
| DataSize::kUSHORT }, |
| |
| // {upper, lower} [] |
| { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength) |
| * DataSize::kUSHORT, |
| DataSize::kUSHORT, |
| 2 * DataSize::kUSHORT, |
| 0, |
| 2 * DataSize::kUSHORT } |
| }; |
| const int32_t kSearchTestOffsetLength = 4; |
| |
| ReadableFontData* |
| FillTestFontDataWithShortsForSearching(WritableFontData* wfd, |
| const int32_t* lower_data, |
| int32_t lower_start_index, |
| int32_t lower_offset, |
| const int32_t* upper_data, |
| int32_t upper_start_index, |
| int32_t upper_offset) { |
| // lower data |
| int offset = lower_start_index; |
| for (int32_t i = 0; i < kLowerByteArrayForSearchingLength; ++i) { |
| wfd->WriteUShort(offset, lower_data[i]); |
| offset += lower_offset; |
| } |
| |
| // upper data |
| offset = upper_start_index; |
| for (int32_t i = 0; i < kUpperByteArrayForSearchingLength; ++i) { |
| wfd->WriteUShort(offset, upper_data[i]); |
| offset += upper_offset; |
| } |
| |
| return wfd; |
| } |
| |
| bool TestReadableFontDataSearching() { |
| for (int32_t i = 0; i < kSearchTestOffsetLength; ++i) { |
| const int32_t* array_setup_offset = SEARCH_TEST_OFFSETS[i]; |
| WritableFontDataPtr wfd; |
| wfd.Attach(WritableFontData::CreateWritableFontData(array_setup_offset[0])); |
| FillTestFontDataWithShortsForSearching(wfd, |
| LOWER_BYTE_ARRAY_FOR_SEARCHING, |
| array_setup_offset[1], |
| array_setup_offset[2], |
| UPPER_BYTE_ARRAY_FOR_SEARCHING, |
| array_setup_offset[3], |
| array_setup_offset[4]); |
| for (int32_t j = 0; j < kSearchTestPairsLength; ++j) { |
| const int32_t* test_case = SEARCH_TEST_PAIRS[j]; |
| int32_t found = wfd->SearchUShort(array_setup_offset[1], |
| array_setup_offset[2], |
| array_setup_offset[3], |
| array_setup_offset[4], |
| kLowerByteArrayForSearchingLength, |
| test_case[0]); |
| #if defined (SFNTLY_DEBUG_FONTDATA) |
| fprintf(stderr, "Searching for %d; Got %d; Expected %d; " |
| "[test %d][offset %d]\n", |
| test_case[0], found, test_case[1], j, i); |
| #endif |
| EXPECT_EQ(test_case[1], found); |
| } |
| } |
| return true; |
| } |
| |
| void FillTestByteArray(ByteArray* ba, int32_t size) { |
| for (int32_t i = 0; i < size; ++i) { |
| ba->Put(i, (byte_t)(i % 256)); |
| } |
| } |
| |
| void ReadFontDataWithSingleByte(ReadableFontData* rfd, ByteVector* buffer) { |
| buffer->resize(rfd->Length()); |
| for (int32_t index = 0; index < rfd->Length(); ++index) { |
| (*buffer)[index] = (byte_t)(rfd->ReadByte(index)); |
| } |
| } |
| |
| void ReadFontDataWithBuffer(ReadableFontData* rfd, |
| int32_t buffer_size, |
| ByteVector* b) { |
| ByteVector buffer(buffer_size); |
| b->resize(rfd->Length()); |
| |
| int32_t index = 0; |
| while (index < rfd->Length()) { |
| int32_t bytes_read = rfd->ReadBytes(index, &(buffer[0]), 0, buffer.size()); |
| EXPECT_GE(bytes_read, 0); |
| std::copy(buffer.begin(), buffer.begin() + bytes_read, b->begin() + index); |
| index += bytes_read; |
| } |
| } |
| |
| void ReadFontDataWithSlidingWindow(ReadableFontData* rfd, int32_t window_size, |
| ByteVector* b) { |
| b->resize(rfd->Length()); |
| int32_t index = 0; |
| while (index < rfd->Length()) { |
| int32_t actual_window_size = |
| std::min<int32_t>(window_size, b->size() - index); |
| int32_t bytes_read = |
| rfd->ReadBytes(index, &((*b)[0]), index, actual_window_size); |
| EXPECT_GE(bytes_read, 0); |
| index += bytes_read; |
| } |
| } |
| |
| void WriteFontDataWithSingleByte(ReadableFontData* rfd, WritableFontData* wfd) { |
| for (int32_t index = 0; index < rfd->Length(); ++index) { |
| byte_t b = (byte_t)(rfd->ReadByte(index)); |
| wfd->WriteByte(index, b); |
| } |
| } |
| |
| void WriteFontDataWithBuffer(ReadableFontData* rfd, |
| WritableFontData* wfd, |
| int32_t buffer_size) { |
| ByteVector buffer(buffer_size); |
| int32_t index = 0; |
| while (index < rfd->Length()) { |
| int32_t bytesRead = rfd->ReadBytes(index, &(buffer[0]), 0, buffer.size()); |
| wfd->WriteBytes(index, &(buffer[0]), 0, buffer.size()); |
| index += bytesRead; |
| } |
| } |
| |
| void WriteFontDataWithSlidingWindow(ReadableFontData* rfd, |
| WritableFontData* wfd, |
| int32_t window_size) { |
| ByteVector b(rfd->Length()); |
| int32_t index = 0; |
| while (index < rfd->Length()) { |
| int32_t sliding_size = std::min<int32_t>(window_size, b.size() - index); |
| int32_t bytes_read = rfd->ReadBytes(index, &(b[0]), index, sliding_size); |
| wfd->WriteBytes(index, &(b[0]), index, sliding_size); |
| index += bytes_read; |
| } |
| } |
| |
| bool ReadComparison(int32_t offset, |
| int32_t length, |
| ReadableFontData* rfd1, |
| ReadableFontData* rfd2) { |
| EXPECT_TRUE(length == rfd2->Length()); |
| ByteVector b1, b2; |
| b1.resize(length); |
| b2.resize(length); |
| |
| // single byte reads |
| ReadFontDataWithSingleByte(rfd1, &b1); |
| ReadFontDataWithSingleByte(rfd2, &b2); |
| EXPECT_EQ(memcmp(&(b1[offset]), &(b2[0]), length), 0); |
| |
| // buffer reads |
| int32_t increments = std::max<int32_t>(length / 11, 1); |
| for (int32_t buffer_size = 1; buffer_size <= length; |
| buffer_size += increments) { |
| b1.clear(); |
| b2.clear(); |
| b1.resize(length); |
| b2.resize(length); |
| ReadFontDataWithBuffer(rfd1, buffer_size, &b1); |
| ReadFontDataWithBuffer(rfd2, buffer_size, &b2); |
| int result = memcmp(&(b1[offset]), &(b2[0]), length); |
| EXPECT_EQ(result, 0); |
| } |
| |
| // sliding window reads |
| for (int32_t window_size = 1; window_size <= length; |
| window_size += increments) { |
| b1.clear(); |
| b2.clear(); |
| b1.resize(length); |
| b2.resize(length); |
| ReadFontDataWithSlidingWindow(rfd1, window_size, &b1); |
| ReadFontDataWithSlidingWindow(rfd2, window_size, &b2); |
| int result = memcmp(&(b1[offset]), &(b2[0]), length); |
| EXPECT_EQ(result, 0); |
| } |
| return true; |
| } |
| |
| void SlicingReadTest(ReadableFontData* rfd) { |
| fprintf(stderr, "read - trim = "); |
| for (int32_t trim = 0; trim < (rfd->Length() / 2) + 1; |
| trim += (rfd->Length() / 21) + 1) { |
| fprintf(stderr, "%d ", trim); |
| int32_t length = rfd->Length() - 2 * trim; |
| ReadableFontDataPtr slice; |
| slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length))); |
| EXPECT_TRUE(ReadComparison(trim, length, rfd, slice)); |
| } |
| fprintf(stderr, "\n"); |
| } |
| |
| void SlicingWriteTest(ReadableFontData* rfd, WritableFontData* wfd) { |
| fprintf(stderr, "write - trim = "); |
| for (int32_t trim = 0; trim < (rfd->Length() / 2) + 1; |
| trim += (rfd->Length() / 21) + 1) { |
| fprintf(stderr, "%d ", trim); |
| int32_t length = rfd->Length() - 2 * trim; |
| WritableFontDataPtr w_slice; |
| ReadableFontDataPtr r_slice; |
| |
| // single byte writes |
| w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length))); |
| r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length))); |
| WriteFontDataWithSingleByte(r_slice, w_slice); |
| EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice)); |
| |
| // buffer writes |
| int32_t increments = std::max<int32_t>(length / 11, 1); |
| for (int32_t buffer_size = 1; buffer_size < length; |
| buffer_size += increments) { |
| w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length))); |
| r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length))); |
| WriteFontDataWithBuffer(r_slice, w_slice, buffer_size); |
| EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice)); |
| } |
| |
| // sliding window writes |
| for (int window_size = 1; window_size < length; window_size += increments) { |
| w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length))); |
| r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length))); |
| WriteFontDataWithSlidingWindow(r_slice, w_slice, window_size); |
| EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice)); |
| } |
| } |
| fprintf(stderr, "\n"); |
| } |
| |
| bool TestReadableFontData() { |
| for (size_t i = 0; i < sizeof(BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) { |
| int32_t size = BYTE_ARRAY_SIZES[i]; |
| ByteArrayPtr ba = new MemoryByteArray(size); |
| FillTestByteArray(ba, size); |
| ReadableFontDataPtr rfd = new ReadableFontData(ba); |
| SlicingReadTest(rfd); |
| } |
| return true; |
| } |
| |
| bool TestWritableFontData() { |
| for (size_t i = 0; i < sizeof(BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) { |
| int32_t size = BYTE_ARRAY_SIZES[i]; |
| ByteArrayPtr ba = new MemoryByteArray(size); |
| FillTestByteArray(ba, size); |
| WritableFontDataPtr wfd = new WritableFontData(ba); |
| SlicingReadTest(wfd); |
| ByteArrayPtr temp = new MemoryByteArray(size); |
| WritableFontDataPtr wfd_copy = new WritableFontData(temp); |
| SlicingWriteTest(wfd, wfd_copy); |
| } |
| return true; |
| } |
| |
| } // namespace sfntly |
| |
| TEST(FontData, ReadableFontDataSearching) { |
| ASSERT_TRUE(sfntly::TestReadableFontDataSearching()); |
| } |
| |
| TEST(FontData, All) { |
| ASSERT_TRUE(sfntly::TestReadableFontData()); |
| ASSERT_TRUE(sfntly::TestWritableFontData()); |
| } |