blob: f01b13c0df825b9ee07d0daf1b14744e3834fb47 [file] [log] [blame]
/*
* 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