| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * 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 "perfetto/ext/base/base64.h" |
| |
| #include "perfetto/ext/base/string_view.h" |
| #include "perfetto/ext/base/utils.h" |
| #include "test/gtest_and_gmock.h" |
| |
| namespace perfetto { |
| namespace base { |
| namespace { |
| |
| struct TestPattern { |
| size_t decoded_len; |
| const char* decoded; |
| const char* encoded; |
| }; |
| |
| TestPattern kPatterns[] = { |
| |
| // Basic bit patterns; |
| // values obtained with "echo -n '...' | uuencode -m test" |
| |
| {1, "\000", "AA=="}, |
| {1, "\001", "AQ=="}, |
| {1, "\002", "Ag=="}, |
| {1, "\004", "BA=="}, |
| {1, "\010", "CA=="}, |
| {1, "\020", "EA=="}, |
| {1, "\040", "IA=="}, |
| {1, "\100", "QA=="}, |
| {1, "\200", "gA=="}, |
| |
| {1, "\377", "/w=="}, |
| {1, "\376", "/g=="}, |
| {1, "\375", "/Q=="}, |
| {1, "\373", "+w=="}, |
| {1, "\367", "9w=="}, |
| {1, "\357", "7w=="}, |
| {1, "\337", "3w=="}, |
| {1, "\277", "vw=="}, |
| {1, "\177", "fw=="}, |
| {2, "\000\000", "AAA="}, |
| {2, "\000\001", "AAE="}, |
| {2, "\000\002", "AAI="}, |
| {2, "\000\004", "AAQ="}, |
| {2, "\000\010", "AAg="}, |
| {2, "\000\020", "ABA="}, |
| {2, "\000\040", "ACA="}, |
| {2, "\000\100", "AEA="}, |
| {2, "\000\200", "AIA="}, |
| {2, "\001\000", "AQA="}, |
| {2, "\002\000", "AgA="}, |
| {2, "\004\000", "BAA="}, |
| {2, "\010\000", "CAA="}, |
| {2, "\020\000", "EAA="}, |
| {2, "\040\000", "IAA="}, |
| {2, "\100\000", "QAA="}, |
| {2, "\200\000", "gAA="}, |
| |
| {2, "\377\377", "//8="}, |
| {2, "\377\376", "//4="}, |
| {2, "\377\375", "//0="}, |
| {2, "\377\373", "//s="}, |
| {2, "\377\367", "//c="}, |
| {2, "\377\357", "/+8="}, |
| {2, "\377\337", "/98="}, |
| {2, "\377\277", "/78="}, |
| {2, "\377\177", "/38="}, |
| {2, "\376\377", "/v8="}, |
| {2, "\375\377", "/f8="}, |
| {2, "\373\377", "+/8="}, |
| {2, "\367\377", "9/8="}, |
| {2, "\357\377", "7/8="}, |
| {2, "\337\377", "3/8="}, |
| {2, "\277\377", "v/8="}, |
| {2, "\177\377", "f/8="}, |
| |
| {3, "\000\000\000", "AAAA"}, |
| {3, "\000\000\001", "AAAB"}, |
| {3, "\000\000\002", "AAAC"}, |
| {3, "\000\000\004", "AAAE"}, |
| {3, "\000\000\010", "AAAI"}, |
| {3, "\000\000\020", "AAAQ"}, |
| {3, "\000\000\040", "AAAg"}, |
| {3, "\000\000\100", "AABA"}, |
| {3, "\000\000\200", "AACA"}, |
| {3, "\000\001\000", "AAEA"}, |
| {3, "\000\002\000", "AAIA"}, |
| {3, "\000\004\000", "AAQA"}, |
| {3, "\000\010\000", "AAgA"}, |
| {3, "\000\020\000", "ABAA"}, |
| {3, "\000\040\000", "ACAA"}, |
| {3, "\000\100\000", "AEAA"}, |
| {3, "\000\200\000", "AIAA"}, |
| {3, "\001\000\000", "AQAA"}, |
| {3, "\002\000\000", "AgAA"}, |
| {3, "\004\000\000", "BAAA"}, |
| {3, "\010\000\000", "CAAA"}, |
| {3, "\020\000\000", "EAAA"}, |
| {3, "\040\000\000", "IAAA"}, |
| {3, "\100\000\000", "QAAA"}, |
| {3, "\200\000\000", "gAAA"}, |
| |
| {3, "\377\377\377", "////"}, |
| {3, "\377\377\376", "///+"}, |
| {3, "\377\377\375", "///9"}, |
| {3, "\377\377\373", "///7"}, |
| {3, "\377\377\367", "///3"}, |
| {3, "\377\377\357", "///v"}, |
| {3, "\377\377\337", "///f"}, |
| {3, "\377\377\277", "//+/"}, |
| {3, "\377\377\177", "//9/"}, |
| {3, "\377\376\377", "//7/"}, |
| {3, "\377\375\377", "//3/"}, |
| {3, "\377\373\377", "//v/"}, |
| {3, "\377\367\377", "//f/"}, |
| {3, "\377\357\377", "/+//"}, |
| {3, "\377\337\377", "/9//"}, |
| {3, "\377\277\377", "/7//"}, |
| {3, "\377\177\377", "/3//"}, |
| {3, "\376\377\377", "/v//"}, |
| {3, "\375\377\377", "/f//"}, |
| {3, "\373\377\377", "+///"}, |
| {3, "\367\377\377", "9///"}, |
| {3, "\357\377\377", "7///"}, |
| {3, "\337\377\377", "3///"}, |
| {3, "\277\377\377", "v///"}, |
| {3, "\177\377\377", "f///"}, |
| |
| // Random numbers: values obtained with |
| // |
| // #! /bin/bash |
| // dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random |
| // od -N $1 -t o1 /tmp/bar.random |
| // uuencode -m test < /tmp/bar.random |
| // |
| // where $1 is the number of bytes (2, 3) |
| |
| {2, "\243\361", "o/E="}, |
| {2, "\024\167", "FHc="}, |
| {2, "\313\252", "y6o="}, |
| {2, "\046\041", "JiE="}, |
| {2, "\145\236", "ZZ4="}, |
| {2, "\254\325", "rNU="}, |
| {2, "\061\330", "Mdg="}, |
| {2, "\245\032", "pRo="}, |
| {2, "\006\000", "BgA="}, |
| {2, "\375\131", "/Vk="}, |
| {2, "\303\210", "w4g="}, |
| {2, "\040\037", "IB8="}, |
| {2, "\261\372", "sfo="}, |
| {2, "\335\014", "3Qw="}, |
| {2, "\233\217", "m48="}, |
| {2, "\373\056", "+y4="}, |
| {2, "\247\232", "p5o="}, |
| {2, "\107\053", "Rys="}, |
| {2, "\204\077", "hD8="}, |
| {2, "\276\211", "vok="}, |
| {2, "\313\110", "y0g="}, |
| {2, "\363\376", "8/4="}, |
| {2, "\251\234", "qZw="}, |
| {2, "\103\262", "Q7I="}, |
| {2, "\142\312", "Yso="}, |
| {2, "\067\211", "N4k="}, |
| {2, "\220\001", "kAE="}, |
| {2, "\152\240", "aqA="}, |
| {2, "\367\061", "9zE="}, |
| {2, "\133\255", "W60="}, |
| {2, "\176\035", "fh0="}, |
| {2, "\032\231", "Gpk="}, |
| |
| {3, "\013\007\144", "Cwdk"}, |
| {3, "\030\112\106", "GEpG"}, |
| {3, "\047\325\046", "J9Um"}, |
| {3, "\310\160\022", "yHAS"}, |
| {3, "\131\100\237", "WUCf"}, |
| {3, "\064\342\134", "NOJc"}, |
| {3, "\010\177\004", "CH8E"}, |
| {3, "\345\147\205", "5WeF"}, |
| {3, "\300\343\360", "wOPw"}, |
| {3, "\061\240\201", "MaCB"}, |
| {3, "\225\333\044", "ldsk"}, |
| {3, "\215\137\352", "jV/q"}, |
| {3, "\371\147\160", "+Wdw"}, |
| {3, "\030\320\051", "GNAp"}, |
| {3, "\044\174\241", "JHyh"}, |
| {3, "\260\127\037", "sFcf"}, |
| {3, "\111\045\033", "SSUb"}, |
| {3, "\202\114\107", "gkxH"}, |
| {3, "\057\371\042", "L/ki"}, |
| {3, "\223\247\244", "k6ek"}, |
| {3, "\047\216\144", "J45k"}, |
| {3, "\203\070\327", "gzjX"}, |
| {3, "\247\140\072", "p2A6"}, |
| {3, "\124\115\116", "VE1O"}, |
| {3, "\157\162\050", "b3Io"}, |
| {3, "\357\223\004", "75ME"}, |
| {3, "\052\117\156", "Kk9u"}, |
| {3, "\347\154\000", "52wA"}, |
| {3, "\303\012\142", "wwpi"}, |
| {3, "\060\035\362", "MB3y"}, |
| {3, "\130\226\361", "WJbx"}, |
| {3, "\173\013\071", "ews5"}, |
| {3, "\336\004\027", "3gQX"}, |
| {3, "\357\366\234", "7/ac"}, |
| {3, "\353\304\111", "68RJ"}, |
| {3, "\024\264\131", "FLRZ"}, |
| {3, "\075\114\251", "PUyp"}, |
| {3, "\315\031\225", "zRmV"}, |
| {3, "\154\201\276", "bIG+"}, |
| {3, "\200\066\072", "gDY6"}, |
| {3, "\142\350\267", "Yui3"}, |
| {3, "\033\000\166", "GwB2"}, |
| {3, "\210\055\077", "iC0/"}, |
| {3, "\341\037\124", "4R9U"}, |
| {3, "\161\103\152", "cUNq"}, |
| {3, "\270\142\131", "uGJZ"}, |
| {3, "\337\076\074", "3z48"}, |
| {3, "\375\106\362", "/Uby"}, |
| {3, "\227\301\127", "l8FX"}, |
| {3, "\340\002\234", "4AKc"}, |
| {3, "\121\064\033", "UTQb"}, |
| {3, "\157\134\143", "b1xj"}, |
| {3, "\247\055\327", "py3X"}, |
| {3, "\340\142\005", "4GIF"}, |
| {3, "\060\260\143", "MLBj"}, |
| {3, "\075\203\170", "PYN4"}, |
| {3, "\143\160\016", "Y3AO"}, |
| {3, "\313\013\063", "ywsz"}, |
| {3, "\174\236\135", "fJ5d"}, |
| {3, "\103\047\026", "QycW"}, |
| {3, "\365\005\343", "9QXj"}, |
| {3, "\271\160\223", "uXCT"}, |
| {3, "\362\255\172", "8q16"}, |
| {3, "\113\012\015", "SwoN"}, |
| |
| // various lengths, generated by this python script: |
| // |
| // from string import lowercase as lc |
| // for i in range(27): |
| // print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i), |
| // lc[:i].encode('base64').strip()) |
| |
| {0, "abcdefghijklmnopqrstuvwxyz", ""}, |
| {1, "abcdefghijklmnopqrstuvwxyz", "YQ=="}, |
| {2, "abcdefghijklmnopqrstuvwxyz", "YWI="}, |
| {3, "abcdefghijklmnopqrstuvwxyz", "YWJj"}, |
| {4, "abcdefghijklmnopqrstuvwxyz", "YWJjZA=="}, |
| {5, "abcdefghijklmnopqrstuvwxyz", "YWJjZGU="}, |
| {6, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVm"}, |
| {7, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZw=="}, |
| {8, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2g="}, |
| {9, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hp"}, |
| {10, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpag=="}, |
| {11, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpams="}, |
| {12, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamts"}, |
| {13, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbQ=="}, |
| {14, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW4="}, |
| {15, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5v"}, |
| {16, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcA=="}, |
| {17, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHE="}, |
| {18, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFy"}, |
| {19, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFycw=="}, |
| {20, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q="}, |
| {21, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1"}, |
| {22, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg=="}, |
| {23, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc="}, |
| {24, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4"}, |
| {25, "abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ=="}, |
| {26, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="}, |
| }; |
| |
| TEST(Base64Test, Encode) { |
| EXPECT_EQ(Base64Encode(""), ""); |
| EXPECT_EQ(Base64Encode("f"), "Zg=="); |
| EXPECT_EQ(Base64Encode("fo"), "Zm8="); |
| EXPECT_EQ(Base64Encode("foo"), "Zm9v"); |
| EXPECT_EQ(Base64Encode("foob"), "Zm9vYg=="); |
| EXPECT_EQ(Base64Encode("fooba"), "Zm9vYmE="); |
| EXPECT_EQ(Base64Encode("foobar"), "Zm9vYmFy"); |
| EXPECT_EQ(Base64Encode("\xff"), "/w=="); |
| EXPECT_EQ(Base64Encode("\xff\xfe"), "//4="); |
| EXPECT_EQ(Base64Encode("\xff\xfe\xfd"), "//79"); |
| EXPECT_EQ(Base64Encode("\xff\xfe\xfd\xfc"), "//79/A=="); |
| |
| for (size_t i = 0; i < ArraySize(kPatterns); ++i) { |
| const auto& p = kPatterns[i]; |
| std::string res = Base64Encode(StringView(p.decoded, p.decoded_len)); |
| EXPECT_EQ(p.encoded, res); |
| } |
| |
| // Error cases |
| char buf[4]; |
| EXPECT_EQ(0, Base64Encode("", 0, buf, 0)); |
| EXPECT_EQ(0, Base64Encode("", 0, buf, 1)); |
| EXPECT_EQ(-1, Base64Encode("a", 1, buf, 0)); |
| EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 0)); |
| EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 1)); |
| EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 3)); |
| EXPECT_EQ(4, Base64Encode("abc", 3, buf, 4)); |
| } |
| |
| TEST(Base64Test, Decode) { |
| EXPECT_EQ(Base64Decode(""), ""); |
| EXPECT_EQ(Base64Decode("Zg=="), "f"); |
| EXPECT_EQ(Base64Decode("Zg="), "f"); |
| EXPECT_EQ(Base64Decode("Zg"), "f"); |
| EXPECT_EQ(Base64Decode("Zm8="), "fo"); |
| EXPECT_EQ(Base64Decode("Zm8"), "fo"); |
| EXPECT_EQ(Base64Decode("Zm9v"), "foo"); |
| EXPECT_EQ(Base64Decode("Zm9vYg=="), "foob"); |
| EXPECT_EQ(Base64Decode("Zm9vYg="), "foob"); |
| EXPECT_EQ(Base64Decode("Zm9vYg"), "foob"); |
| EXPECT_EQ(Base64Decode("Zm9vYmE="), "fooba"); |
| EXPECT_EQ(Base64Decode("Zm9vYmE"), "fooba"); |
| EXPECT_EQ(Base64Decode("Zm9vYmFy"), "foobar"); |
| EXPECT_EQ(Base64Decode("/w=="), "\xff"); |
| EXPECT_EQ(Base64Decode("/w="), "\xff"); |
| EXPECT_EQ(Base64Decode("/w"), "\xff"); |
| EXPECT_EQ(Base64Decode("//4="), "\xff\xfe"); |
| EXPECT_EQ(Base64Decode("//4"), "\xff\xfe"); |
| EXPECT_EQ(Base64Decode("//79"), "\xff\xfe\xfd"); |
| EXPECT_EQ(Base64Decode("//79/A=="), "\xff\xfe\xfd\xfc"); |
| EXPECT_EQ(Base64Decode("//79/A="), "\xff\xfe\xfd\xfc"); |
| EXPECT_EQ(Base64Decode("//79/A"), "\xff\xfe\xfd\xfc"); |
| |
| for (size_t i = 0; i < ArraySize(kPatterns); ++i) { |
| const auto& p = kPatterns[i]; |
| std::optional<std::string> dec = Base64Decode(StringView(p.encoded)); |
| EXPECT_TRUE(dec.has_value()); |
| EXPECT_EQ(dec.value(), StringView(p.decoded, p.decoded_len).ToStdString()); |
| } |
| |
| // Error cases: |
| EXPECT_EQ(Base64Decode("Z"), std::nullopt); |
| EXPECT_EQ(Base64Decode("Zm9vY"), std::nullopt); |
| |
| uint8_t buf[4]; |
| EXPECT_EQ(Base64Decode("", 0, buf, 2), 0); // Valid, 0 len. |
| EXPECT_EQ(Base64Decode("Z", 1, buf, 1), -1); // Invalid input. |
| EXPECT_EQ(Base64Decode("Zg==", 4, buf, 1), -1); // Not enough dst space. |
| } |
| |
| } // namespace |
| } // namespace base |
| } // namespace perfetto |