/* * 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 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