diff options
Diffstat (limited to 'core/fxcodec/codec')
-rw-r--r-- | core/fxcodec/codec/fx_codec.cpp | 142 | ||||
-rw-r--r-- | core/fxcodec/codec/fx_codec_a85_unittest.cpp | 209 | ||||
-rw-r--r-- | core/fxcodec/codec/fx_codec_rle_unittest.cpp | 195 |
3 files changed, 544 insertions, 2 deletions
diff --git a/core/fxcodec/codec/fx_codec.cpp b/core/fxcodec/codec/fx_codec.cpp index 6b6c723388..23171cf3ea 100644 --- a/core/fxcodec/codec/fx_codec.cpp +++ b/core/fxcodec/codec/fx_codec.cpp @@ -6,6 +6,7 @@ #include "core/fxcodec/fx_codec.h" +#include <algorithm> #include <cmath> #include <memory> #include <utility> @@ -101,14 +102,151 @@ bool CCodec_BasicModule::RunLengthEncode(const uint8_t* src_buf, uint32_t src_size, uint8_t** dest_buf, uint32_t* dest_size) { - return false; + // Check inputs + if (!src_buf || !dest_buf || !dest_size || src_size == 0) + return false; + + // Edge case + if (src_size == 1) { + *dest_buf = FX_Alloc(uint8_t, 3); + (*dest_buf)[0] = 0; + (*dest_buf)[1] = src_buf[0]; + (*dest_buf)[2] = 128; + *dest_size = 3; + return true; + } + + // Worst case: 1 nonmatch, 2 match, 1 nonmatch, 2 match, etc. This becomes + // 4 output chars for every 3 input, plus up to 4 more for the 1-2 chars + // rounded off plus the terminating character. + uint32_t est_size = 4 * ((src_size + 2) / 3) + 1; + *dest_buf = FX_Alloc(uint8_t, est_size); + + // Set up pointers. + uint8_t* out = *dest_buf; + uint32_t run_start = 0; + uint32_t run_end = 1; + uint8_t x = src_buf[run_start]; + uint8_t y = src_buf[run_end]; + while (run_end < src_size) { + uint32_t max_len = std::min((uint32_t)128, src_size - run_start); + while (x == y && (run_end - run_start < max_len - 1)) + y = src_buf[++run_end]; + + // Reached end with matched run. Update variables to expected values. + if (x == y) { + run_end++; + if (run_end < src_size) + y = src_buf[run_end]; + } + if (run_end - run_start > 1) { // Matched run but not at end of input. + out[0] = 257 - (run_end - run_start); + out[1] = x; + x = y; + run_start = run_end; + run_end++; + if (run_end < src_size) + y = src_buf[run_end]; + out += 2; + continue; + } + // Mismatched run + while (x != y && run_end <= run_start + max_len) { + out[run_end - run_start] = x; + x = y; + run_end++; + if (run_end == src_size) { + if (run_end <= run_start + max_len) { + out[run_end - run_start] = x; + run_end++; + } + break; + } + y = src_buf[run_end]; + } + out[0] = run_end - run_start - 2; + out += run_end - run_start; + run_start = run_end - 1; + } + if (run_start < src_size) { // 1 leftover character + out[0] = 0; + out[1] = x; + out += 2; + } + *out = 128; + *dest_size = out + 1 - *dest_buf; + return true; } bool CCodec_BasicModule::A85Encode(const uint8_t* src_buf, uint32_t src_size, uint8_t** dest_buf, uint32_t* dest_size) { - return false; + // Check inputs. + if (!src_buf || !dest_buf || !dest_size) + return false; + + if (src_size == 0) { + *dest_size = 0; + return false; + } + + // Worst case: 5 output for each 4 input (plus up to 4 from leftover), plus + // 2 character new lines each 75 output chars plus 2 termination chars. May + // have fewer if there are special "z" chars. + uint32_t est_size = 5 * (src_size / 4) + 4 + src_size / 30 + 2; + *dest_buf = FX_Alloc(uint8_t, est_size); + + // Set up pointers. + uint8_t* out = *dest_buf; + uint32_t pos = 0; + uint32_t line_length = 0; + while (src_size >= 4 && pos < src_size - 3) { + uint32_t val = ((uint32_t)(src_buf[pos]) << 24) + + ((uint32_t)(src_buf[pos + 1]) << 16) + + ((uint32_t)(src_buf[pos + 2]) << 8) + + (uint32_t)(src_buf[pos + 3]); + pos += 4; + if (val == 0) { // All zero special case + *out = 'z'; + out++; + line_length++; + } else { // Compute base 85 characters and add 33. + for (int i = 4; i >= 0; i--) { + out[i] = (uint8_t)(val % 85) + 33; + val = val / 85; + } + out += 5; + line_length += 5; + } + if (line_length >= 75) { // Add a return. + *out++ = '\r'; + *out++ = '\n'; + line_length = 0; + } + } + if (pos < src_size) { // Leftover bytes + uint32_t val = 0; + int count = 0; + while (pos < src_size) { + val += (uint32_t)(src_buf[pos] << (8 * (3 - pos))); + count++; + pos++; + } + for (int i = 4; i >= 0; i--) { + if (i <= count) + out[i] = (uint8_t)(val % 85) + 33; + val = val / 85; + } + out += count + 1; + } + + // Terminating characters. + out[0] = '~'; + out[1] = '>'; + out += 2; + *dest_size = out - *dest_buf; + return true; } #ifdef PDF_ENABLE_XFA diff --git a/core/fxcodec/codec/fx_codec_a85_unittest.cpp b/core/fxcodec/codec/fx_codec_a85_unittest.cpp new file mode 100644 index 0000000000..7910881772 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_a85_unittest.cpp @@ -0,0 +1,209 @@ +// Copyright 2016 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdint.h> + +#include <limits> + +#include "core/fxcodec/codec/ccodec_basicmodule.h" +#include "core/fxcodec/fx_codec.h" +#include "testing/fx_string_testhelpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(fxcodec, A85TestBadInputs) { + uint8_t src_buf[4] = {1, 2, 3, 4}; + uint8_t* dest_buf = nullptr; + uint32_t src_size = 4; + uint32_t dest_size = 0; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + // Error codes, not segvs, should callers pass us a nullptr pointer. + EXPECT_FALSE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, nullptr)); + EXPECT_FALSE(pEncoders->A85Encode(src_buf, src_size, nullptr, &dest_size)); + EXPECT_FALSE(pEncoders->A85Encode(src_buf, 0, &dest_buf, &dest_size)); + EXPECT_FALSE(pEncoders->A85Encode(nullptr, src_size, &dest_buf, &dest_size)); +} + +// No leftover bytes, just translate 2 sets of symbols. +TEST(fxcodec, A85TestBasic) { + // Make sure really big values don't break. + uint8_t src_buf[8] = {1, 2, 3, 4, 255, 255, 255, 255}; + uint8_t* dest_buf = nullptr; + uint32_t src_size = 8; + uint32_t dest_size = 0; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + // Should succeed. + EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size)); + + // Should have 5 chars for each set of 4 and 2 terminators. + EXPECT_EQ(12u, dest_size); + uint8_t expected_out[12] = {33, 60, 78, 63, 43, 115, 56, 87, 45, 33, 126, 62}; + + // Check the output + for (uint32_t i = 0; i < 12; i++) + EXPECT_EQ(dest_buf[i], expected_out[i]) << " at " << i; + FX_Free(dest_buf); +} + +// Leftover bytes. +TEST(fxcodec, A85TestLeftoverBytes) { + // 1 Leftover Byte: + uint8_t src_buf_1leftover[5] = {1, 2, 3, 4, 255}; + uint8_t* dest_buf = nullptr; + uint32_t src_size = 5; + uint32_t dest_size = 0; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + // Should succeed + EXPECT_TRUE( + pEncoders->A85Encode(src_buf_1leftover, src_size, &dest_buf, &dest_size)); + EXPECT_EQ(9u, dest_size); // 5 chars for first symbol + 2 + 2 terminators. + uint8_t expected_out_1leftover[9] = {33, 60, 78, 63, 43, 114, 114, 126, 62}; + + // Check the output + for (uint32_t i = 0; i < 9; i++) + EXPECT_EQ(dest_buf[i], expected_out_1leftover[i]) << " at " << i; + FX_Free(dest_buf); + + // 2 Leftover bytes: + src_size++; + dest_buf = nullptr; + dest_size = 0; + uint8_t src_buf_2leftover[6] = {1, 2, 3, 4, 255, 254}; + // Should succeed + EXPECT_TRUE( + pEncoders->A85Encode(src_buf_2leftover, src_size, &dest_buf, &dest_size)); + EXPECT_EQ(10u, dest_size); // 5 chars for first symbol + 3 + 2 terminators. + uint8_t expected_out_2leftover[10] = {33, 60, 78, 63, 43, + 115, 56, 68, 126, 62}; + + // Check the output + for (uint32_t i = 0; i < 10; i++) + EXPECT_EQ(dest_buf[i], expected_out_2leftover[i]) << " at " << i; + FX_Free(dest_buf); + + // 3 Leftover bytes: + src_size++; + dest_buf = nullptr; + dest_size = 0; + uint8_t src_buf_3leftover[7] = {1, 2, 3, 4, 255, 254, 253}; + // Should succeed + EXPECT_TRUE( + pEncoders->A85Encode(src_buf_3leftover, src_size, &dest_buf, &dest_size)); + EXPECT_EQ(11u, dest_size); // 5 chars for first symbol + 4 + 2 terminators. + uint8_t expected_out_3leftover[11] = {33, 60, 78, 63, 43, 115, + 56, 77, 114, 126, 62}; + + // Check the output + for (uint32_t i = 0; i < 11; i++) + EXPECT_EQ(dest_buf[i], expected_out_3leftover[i]) << " at " << i; + FX_Free(dest_buf); +} + +// Test all zeros comes through as "z". +TEST(fxcodec, A85TestZeros) { + // Make sure really big values don't break. + uint8_t src_buf[8] = {1, 2, 3, 4, 0, 0, 0, 0}; + uint8_t* dest_buf = nullptr; + uint32_t src_size = 8; + uint32_t dest_size = 0; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + // Should succeed. + EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size)); + + // Should have 5 chars for first set of 4 + 1 for z + 2 terminators. + EXPECT_EQ(8u, dest_size); + uint8_t expected_out[8] = {33, 60, 78, 63, 43, 122, 126, 62}; + + // Check the output + for (uint32_t i = 0; i < 8; i++) + EXPECT_EQ(dest_buf[i], expected_out[i]) << " at " << i; + FX_Free(dest_buf); + + // Should also work if it is at the start: + dest_buf = nullptr; + dest_size = 0; + uint8_t src_buf_2[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + + // Should succeed. + EXPECT_TRUE(pEncoders->A85Encode(src_buf_2, src_size, &dest_buf, &dest_size)); + + // Should have 5 chars for set of 4 + 1 for z + 2 terminators. + EXPECT_EQ(8u, dest_size); + uint8_t expected_out_2[8] = {122, 33, 60, 78, 63, 43, 126, 62}; + + // Check the output + for (uint32_t i = 0; i < 8; i++) + EXPECT_EQ(dest_buf[i], expected_out_2[i]) << " at " << i; + FX_Free(dest_buf); + + // Try with 2 leftover zero bytes. Make sure we don't get a "z". + src_size = 6; // Cut off the last 2 zeros. + dest_buf = nullptr; + dest_size = 0; + + // Should succeed. + EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size)); + + // Should have 5 chars for set of 4 + 3 for last 2 + 2 terminators. + EXPECT_EQ(10u, dest_size); + uint8_t expected_out_leftover[10] = {33, 60, 78, 63, 43, 33, 33, 33, 126, 62}; + + // Check the output + for (uint32_t i = 0; i < 10; i++) + EXPECT_EQ(dest_buf[i], expected_out_leftover[i]) << " at " << i; + FX_Free(dest_buf); +} + +// Make sure we get returns in the expected locations. +TEST(fxcodec, A85TestLineBreaks) { + // Make sure really big values don't break. + uint8_t src_buf[131] = {0}; + // 1 full line + most of a line of normal symbols. + for (int k = 0; k < 116; k += 4) { + src_buf[k] = 1; + src_buf[k + 1] = 2; + src_buf[k + 2] = 3; + src_buf[k + 3] = 4; + } + // Fill in the end, leaving an all zero gap + 3 extra zeros at the end. + for (int k = 120; k < 128; k++) { + src_buf[k] = 1; + src_buf[k + 1] = 2; + src_buf[k + 2] = 3; + src_buf[k + 3] = 4; + } + uint8_t* dest_buf = nullptr; + uint32_t src_size = 131; + uint32_t dest_size = 0; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + // Should succeed. + EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size)); + + // Should have 75 chars in the first row plus 2 char return, + // 76 chars in the second row plus 2 char return, + // and 9 chars in the last row with 2 terminators. + EXPECT_EQ(166u, dest_size); + + // Check for the returns. + EXPECT_EQ(dest_buf[75], 13); + EXPECT_EQ(dest_buf[76], 10); + EXPECT_EQ(dest_buf[153], 13); + EXPECT_EQ(dest_buf[154], 10); + + FX_Free(dest_buf); +} diff --git a/core/fxcodec/codec/fx_codec_rle_unittest.cpp b/core/fxcodec/codec/fx_codec_rle_unittest.cpp new file mode 100644 index 0000000000..94d87cde92 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_rle_unittest.cpp @@ -0,0 +1,195 @@ +// Copyright 2016 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdint.h> + +#include <limits> + +#include "core/fpdfapi/parser/fpdf_parser_decode.h" +#include "core/fxcodec/codec/ccodec_basicmodule.h" +#include "core/fxcodec/fx_codec.h" +#include "testing/fx_string_testhelpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(fxcodec, RLETestBadInputs) { + uint8_t src_buf[1] = {1}; + uint8_t* dest_buf = nullptr; + uint32_t src_size = 4; + uint32_t dest_size = 0; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + // Error codes, not segvs, should callers pass us a nullptr pointer. + EXPECT_FALSE( + pEncoders->RunLengthEncode(src_buf, src_size, &dest_buf, nullptr)); + EXPECT_FALSE( + pEncoders->RunLengthEncode(src_buf, src_size, nullptr, &dest_size)); + EXPECT_FALSE(pEncoders->RunLengthEncode(src_buf, 0, &dest_buf, &dest_size)); + EXPECT_FALSE( + pEncoders->RunLengthEncode(nullptr, src_size, &dest_buf, &dest_size)); +} + +// Check length 1 input works. Check terminating character is applied. +TEST(fxcodec, RLETestShortInput) { + uint8_t src_buf[1] = {1}; + uint8_t* dest_buf = nullptr; + uint32_t src_size = 1; + uint32_t dest_size = 0; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + EXPECT_TRUE( + pEncoders->RunLengthEncode(src_buf, src_size, &dest_buf, &dest_size)); + EXPECT_EQ(3u, dest_size); + EXPECT_EQ(dest_buf[0], 0); + EXPECT_EQ(dest_buf[1], 1); + EXPECT_EQ(dest_buf[2], 128); + + FX_Free(dest_buf); +} + +// Check a few basic cases (2 matching runs in a row, matching run followed +// by a nonmatching run, and nonmatching run followed by a matching run). +TEST(fxcodec, RLETestNormalInputs) { + // Match, match + uint8_t src_buf_1[10] = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4}; + + // Match, nonmatch + uint8_t src_buf_2[10] = {2, 2, 2, 2, 1, 2, 3, 4, 5, 6}; + + // Nonmatch, match + uint8_t src_buf_3[10] = {1, 2, 3, 4, 5, 3, 3, 3, 3, 3}; + + uint32_t src_size = 10; + uint32_t dest_size = 0; + uint8_t* dest_buf = nullptr; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + // Case 1: + EXPECT_TRUE( + pEncoders->RunLengthEncode(src_buf_1, src_size, &dest_buf, &dest_size)); + uint8_t* decoded_buf = nullptr; + uint32_t decoded_size = 0; + RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size); + EXPECT_EQ(decoded_size, src_size); + for (uint32_t i = 0; i < src_size; i++) + EXPECT_EQ(decoded_buf[i], src_buf_1[i]) << " at " << i; + FX_Free(dest_buf); + FX_Free(decoded_buf); + + // Case 2: + dest_buf = nullptr; + dest_size = 0; + EXPECT_TRUE( + pEncoders->RunLengthEncode(src_buf_2, src_size, &dest_buf, &dest_size)); + decoded_buf = nullptr; + decoded_size = 0; + RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size); + EXPECT_EQ(decoded_size, src_size); + for (uint32_t i = 0; i < src_size; i++) + EXPECT_EQ(decoded_buf[i], src_buf_2[i]) << " at " << i; + FX_Free(dest_buf); + FX_Free(decoded_buf); + + // Case 3: + dest_buf = nullptr; + dest_size = 0; + EXPECT_TRUE( + pEncoders->RunLengthEncode(src_buf_3, src_size, &dest_buf, &dest_size)); + decoded_buf = nullptr; + decoded_size = 0; + RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size); + EXPECT_EQ(decoded_size, src_size); + for (uint32_t i = 0; i < src_size; i++) + EXPECT_EQ(decoded_buf[i], src_buf_3[i]) << " at " << i; + FX_Free(dest_buf); + FX_Free(decoded_buf); +} + +// Check that runs longer than 128 are broken up properly, both matched and +// nonmatched. +TEST(fxcodec, RLETestFullLengthInputs) { + // Match, match + uint8_t src_buf_1[260] = {1}; + + // Match, nonmatch + uint8_t src_buf_2[260] = {2}; + for (uint16_t i = 128; i < 260; i++) + src_buf_2[i] = (uint8_t)(i - 125); + + // Nonmatch, match + uint8_t src_buf_3[260] = {3}; + for (uint8_t i = 0; i < 128; i++) + src_buf_3[i] = i; + + // Nonmatch, nonmatch + uint8_t src_buf_4[260]; + for (uint16_t i = 0; i < 260; i++) + src_buf_4[i] = (uint8_t)(i); + + uint32_t src_size = 260; + uint32_t dest_size = 0; + uint8_t* dest_buf = nullptr; + + CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule(); + EXPECT_TRUE(pEncoders); + + // Case 1: + EXPECT_TRUE( + pEncoders->RunLengthEncode(src_buf_1, src_size, &dest_buf, &dest_size)); + uint8_t* decoded_buf = nullptr; + uint32_t decoded_size = 0; + RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size); + EXPECT_EQ(decoded_size, src_size); + for (uint32_t i = 0; i < src_size; i++) + EXPECT_EQ(decoded_buf[i], src_buf_1[i]) << " at " << i; + FX_Free(dest_buf); + FX_Free(decoded_buf); + + // Case 2: + dest_buf = nullptr; + dest_size = 0; + EXPECT_TRUE( + pEncoders->RunLengthEncode(src_buf_2, src_size, &dest_buf, &dest_size)); + decoded_buf = nullptr; + decoded_size = 0; + RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size); + EXPECT_EQ(decoded_size, src_size); + for (uint32_t i = 0; i < src_size; i++) + EXPECT_EQ(decoded_buf[i], src_buf_2[i]) << " at " << i; + FX_Free(dest_buf); + FX_Free(decoded_buf); + + // Case 3: + dest_buf = nullptr; + dest_size = 0; + EXPECT_TRUE( + pEncoders->RunLengthEncode(src_buf_3, src_size, &dest_buf, &dest_size)); + decoded_buf = nullptr; + decoded_size = 0; + RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size); + EXPECT_EQ(decoded_size, src_size); + for (uint32_t i = 0; i < src_size; i++) + EXPECT_EQ(decoded_buf[i], src_buf_3[i]) << " at " << i; + FX_Free(dest_buf); + FX_Free(decoded_buf); + + // Case 4: + dest_buf = nullptr; + dest_size = 0; + EXPECT_TRUE( + pEncoders->RunLengthEncode(src_buf_4, src_size, &dest_buf, &dest_size)); + decoded_buf = nullptr; + decoded_size = 0; + RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size); + EXPECT_EQ(decoded_size, src_size); + for (uint32_t i = 0; i < src_size; i++) + EXPECT_EQ(decoded_buf[i], src_buf_4[i]) << " at " << i; + FX_Free(dest_buf); + FX_Free(decoded_buf); +} |