Global Metrics
path: .metrics.mi.mi_visual_studio
old: 28.2529055154994
new: 0.0
path: .metrics.mi.mi_sei
old: 18.0491423406364
new: -66.79152177080516
path: .metrics.mi.mi_original
old: 48.31246843150397
new: -19.755685607649767
path: .metrics.halstead.bugs
old: 0.780928412061825
new: 8.988633279004251
path: .metrics.halstead.n1
old: 14.0
new: 47.0
path: .metrics.halstead.N2
old: 272.0
new: 1534.0
path: .metrics.halstead.effort
old: 113396.2565401223
new: 4428150.527297475
path: .metrics.halstead.purity_ratio
old: 0.7148276483126781
new: 0.6015907247521665
path: .metrics.halstead.level
old: 0.03256302521008403
new: 0.006879525090848568
path: .metrics.halstead.time
old: 6299.792030006794
new: 246008.3626276375
path: .metrics.halstead.n2
old: 62.0
new: 248.0
path: .metrics.halstead.volume
old: 3692.5251604451582
new: 30463.572658597295
path: .metrics.halstead.vocabulary
old: 76.0
new: 295.0
path: .metrics.halstead.estimated_program_length
old: 422.46314015279273
new: 2233.706361004794
path: .metrics.halstead.length
old: 591.0
new: 3713.0
path: .metrics.halstead.difficulty
old: 30.70967741935484
new: 145.35887096774192
path: .metrics.halstead.N1
old: 319.0
new: 2179.0
path: .metrics.nargs.average
old: 1.5
new: 2.787878787878788
path: .metrics.nargs.sum
old: 3.0
new: 92.0
path: .metrics.nexits.sum
old: 1.0
new: 69.0
path: .metrics.nexits.average
old: 0.5
new: 2.090909090909091
path: .metrics.cyclomatic.average
old: 1.0
new: 3.4594594594594597
path: .metrics.cyclomatic.sum
old: 16.0
new: 128.0
path: .metrics.cognitive.sum
old: 0.0
new: 106.0
path: .metrics.cognitive.average
old: 0.0
new: 3.212121212121212
path: .metrics.loc.ploc
old: 76.0
new: 595.0
path: .metrics.loc.lloc
old: 39.0
new: 292.0
path: .metrics.loc.cloc
old: 10.0
new: 83.0
path: .metrics.loc.blank
old: 25.0
new: 90.0
path: .metrics.loc.sloc
old: 111.0
new: 768.0
path: .metrics.nom.functions
old: 2.0
new: 33.0
path: .metrics.nom.total
old: 2.0
new: 33.0
Spaces Data
Minimal test - lines (335, 339)
path: .spaces[1].spaces[0].metrics.nom.functions
old: 0.0
new: 1.0
path: .spaces[1].spaces[0].metrics.nom.total
old: 0.0
new: 1.0
path: .spaces[1].spaces[0].metrics.nexits.average
old: null
new: 1.0
path: .spaces[1].spaces[0].metrics.nexits.sum
old: 0.0
new: 1.0
path: .spaces[1].spaces[0].metrics.halstead.n1
old: 0.0
new: 10.0
path: .spaces[1].spaces[0].metrics.halstead.time
old: 0.0
new: 56.90125126032457
path: .spaces[1].spaces[0].metrics.halstead.difficulty
old: 0.0
new: 7.777777777777778
path: .spaces[1].spaces[0].metrics.halstead.purity_ratio
old: null
new: 1.9918905148985304
path: .spaces[1].spaces[0].metrics.halstead.volume
old: 0.0
new: 131.68575291675114
path: .spaces[1].spaces[0].metrics.halstead.N2
old: 1.0
new: 14.0
path: .spaces[1].spaces[0].metrics.halstead.effort
old: 0.0
new: 1024.2225226858425
path: .spaces[1].spaces[0].metrics.halstead.N1
old: 0.0
new: 17.0
path: .spaces[1].spaces[0].metrics.halstead.estimated_program_length
old: null
new: 61.74860596185444
path: .spaces[1].spaces[0].metrics.halstead.n2
old: 1.0
new: 9.0
path: .spaces[1].spaces[0].metrics.halstead.length
old: 1.0
new: 31.0
path: .spaces[1].spaces[0].metrics.halstead.level
old: null
new: 0.1285714285714286
path: .spaces[1].spaces[0].metrics.halstead.bugs
old: 0.0
new: 0.033869461608008876
path: .spaces[1].spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 19.0
path: .spaces[1].spaces[0].metrics.loc.lloc
old: 0.0
new: 1.0
path: .spaces[1].spaces[0].metrics.loc.ploc
old: 1.0
new: 5.0
path: .spaces[1].spaces[0].metrics.loc.sloc
old: 1.0
new: 5.0
path: .spaces[1].spaces[0].metrics.nargs.average
old: null
new: 4.0
path: .spaces[1].spaces[0].metrics.nargs.sum
old: 0.0
new: 4.0
path: .spaces[1].spaces[0].metrics.cognitive.average
old: null
new: 0.0
path: .spaces[1].spaces[0].metrics.mi.mi_original
old: null
new: 119.31893001070534
path: .spaces[1].spaces[0].metrics.mi.mi_sei
old: null
new: 96.54179647801362
path: .spaces[1].spaces[0].metrics.mi.mi_visual_studio
old: null
new: 69.77715205304406
Code
nsresult Base64EncodeInputStream(nsIInputStream* aInputStream,
nsACString& aDest, uint32_t aCount,
uint32_t aOffset) {
return EncodeInputStream(aInputStream, aDest, aCount, aOffset);
}
Minimal test - lines (333, 768)
path: .spaces[1].metrics.halstead.estimated_program_length
old: 12.754887502163468
new: 993.3620423942308
path: .spaces[1].metrics.halstead.vocabulary
old: 7.0
new: 154.0
path: .spaces[1].metrics.halstead.time
old: 1.871569948038403
new: 113923.9559737632
path: .spaces[1].metrics.halstead.N1
old: 4.0
new: 1234.0
path: .spaces[1].metrics.halstead.effort
old: 33.68825906469125
new: 2050631.2075277376
path: .spaces[1].metrics.halstead.level
old: 0.6666666666666666
new: 0.007253918693033084
path: .spaces[1].metrics.halstead.purity_ratio
old: 1.5943609377704335
new: 0.4852770114285446
path: .spaces[1].metrics.halstead.difficulty
old: 1.5
new: 137.85652173913044
path: .spaces[1].metrics.halstead.length
old: 8.0
new: 2047.0
path: .spaces[1].metrics.halstead.N2
old: 4.0
new: 813.0
path: .spaces[1].metrics.halstead.n1
old: 3.0
new: 39.0
path: .spaces[1].metrics.halstead.bugs
old: 0.003476944758806696
new: 5.380266485518782
path: .spaces[1].metrics.halstead.volume
old: 22.458839376460833
new: 14875.112048802464
path: .spaces[1].metrics.halstead.n2
old: 4.0
new: 115.0
path: .spaces[1].metrics.nargs.sum
old: 0.0
new: 66.0
path: .spaces[1].metrics.nargs.average
old: null
new: 2.869565217391304
path: .spaces[1].metrics.mi.mi_sei
old: 114.79610035996174
new: -46.07906252264767
path: .spaces[1].metrics.mi.mi_original
old: 131.90127313109093
new: 2.573482889739722
path: .spaces[1].metrics.mi.mi_visual_studio
old: 77.13524744508241
new: 1.5049607542337555
path: .spaces[1].metrics.nexits.sum
old: 0.0
new: 55.0
path: .spaces[1].metrics.nexits.average
old: null
new: 2.391304347826087
path: .spaces[1].metrics.nom.functions
old: 0.0
new: 23.0
path: .spaces[1].metrics.nom.total
old: 0.0
new: 23.0
path: .spaces[1].metrics.cognitive.sum
old: 0.0
new: 70.0
path: .spaces[1].metrics.cognitive.average
old: null
new: 3.0434782608695654
path: .spaces[1].metrics.cyclomatic.sum
old: 2.0
new: 87.0
path: .spaces[1].metrics.cyclomatic.average
old: 1.0
new: 3.625
path: .spaces[1].metrics.loc.lloc
old: 0.0
new: 184.0
path: .spaces[1].metrics.loc.ploc
old: 4.0
new: 345.0
path: .spaces[1].metrics.loc.cloc
old: 0.0
new: 22.0
path: .spaces[1].metrics.loc.sloc
old: 4.0
new: 436.0
path: .spaces[1].metrics.loc.blank
old: 0.0
new: 69.0
Code
namespace mozilla {
nsresult Base64EncodeInputStream(nsIInputStream* aInputStream,
nsACString& aDest, uint32_t aCount,
uint32_t aOffset) {
return EncodeInputStream(aInputStream, aDest, aCount, aOffset);
}
nsresult Base64EncodeInputStream(nsIInputStream* aInputStream, nsAString& aDest,
uint32_t aCount, uint32_t aOffset) {
return EncodeInputStream(aInputStream, aDest, aCount, aOffset);
}
nsresult Base64Encode(const char* aBinary, uint32_t aBinaryLen,
char** aBase64) {
if (aBinaryLen == 0) {
*aBase64 = (char*)moz_xmalloc(1);
(*aBase64)[0] = '\0';
return NS_OK;
}
const auto base64LenOrErr = CalculateBase64EncodedLength(aBinaryLen);
if (base64LenOrErr.isErr()) {
return base64LenOrErr.inspectErr();
}
const uint32_t base64Len = base64LenOrErr.inspect();
*aBase64 = nullptr;
// Add one byte for null termination.
UniqueFreePtr base64((char*)malloc(base64Len + 1));
if (!base64) {
return NS_ERROR_OUT_OF_MEMORY;
}
Encode(aBinary, aBinaryLen, base64.get());
base64[base64Len] = '\0';
*aBase64 = base64.release();
return NS_OK;
}
template
static nsresult Base64EncodeHelper(const T* const aBinary,
const size_t aBinaryLen, U& aBase64) {
if (aBinaryLen == 0) {
if (!Append) {
aBase64.Truncate();
}
return NS_OK;
}
const uint32_t prefixLen = Append ? aBase64.Length() : 0;
const auto base64LenOrErr =
CalculateBase64EncodedLength(aBinaryLen, prefixLen);
if (base64LenOrErr.isErr()) {
return base64LenOrErr.inspectErr();
}
const uint32_t base64Len = base64LenOrErr.inspect();
auto handleOrErr = aBase64.BulkWrite(base64Len, prefixLen, false);
if (handleOrErr.isErr()) {
return handleOrErr.unwrapErr();
}
auto handle = handleOrErr.unwrap();
Encode(aBinary, aBinaryLen, handle.Elements() + prefixLen);
handle.Finish(base64Len, false);
return NS_OK;
}
nsresult Base64EncodeAppend(const char* aBinary, uint32_t aBinaryLen,
nsAString& aBase64) {
return Base64EncodeHelper(aBinary, aBinaryLen, aBase64);
}
nsresult Base64EncodeAppend(const char* aBinary, uint32_t aBinaryLen,
nsACString& aBase64) {
return Base64EncodeHelper(aBinary, aBinaryLen, aBase64);
}
nsresult Base64EncodeAppend(const nsACString& aBinary, nsACString& aBase64) {
return Base64EncodeHelper(aBinary.BeginReading(), aBinary.Length(),
aBase64);
}
nsresult Base64EncodeAppend(const nsACString& aBinary, nsAString& aBase64) {
return Base64EncodeHelper(aBinary.BeginReading(), aBinary.Length(),
aBase64);
}
nsresult Base64Encode(const char* aBinary, uint32_t aBinaryLen,
nsACString& aBase64) {
return Base64EncodeHelper(aBinary, aBinaryLen, aBase64);
}
nsresult Base64Encode(const char* aBinary, uint32_t aBinaryLen,
nsAString& aBase64) {
return Base64EncodeHelper(aBinary, aBinaryLen, aBase64);
}
nsresult Base64Encode(const nsACString& aBinary, nsACString& aBase64) {
return Base64EncodeHelper(aBinary.BeginReading(), aBinary.Length(), aBase64);
}
nsresult Base64Encode(const nsACString& aBinary, nsAString& aBase64) {
return Base64EncodeHelper(aBinary.BeginReading(), aBinary.Length(), aBase64);
}
nsresult Base64Encode(const nsAString& aBinary, nsAString& aBase64) {
return Base64EncodeHelper(aBinary.BeginReading(), aBinary.Length(), aBase64);
}
template
static bool Decode4to3(const T* aSrc, U* aDest, Decoder aToVal) {
uint8_t w, x, y, z;
if (!aToVal(aSrc[0], &w) || !aToVal(aSrc[1], &x) || !aToVal(aSrc[2], &y) ||
!aToVal(aSrc[3], &z)) {
return false;
}
aDest[0] = U(uint8_t(w << 2 | x >> 4));
aDest[1] = U(uint8_t(x << 4 | y >> 2));
aDest[2] = U(uint8_t(y << 6 | z));
return true;
}
template
static bool Decode3to2(const T* aSrc, U* aDest, Decoder aToVal) {
uint8_t w, x, y;
if (!aToVal(aSrc[0], &w) || !aToVal(aSrc[1], &x) || !aToVal(aSrc[2], &y)) {
return false;
}
aDest[0] = U(uint8_t(w << 2 | x >> 4));
aDest[1] = U(uint8_t(x << 4 | y >> 2));
return true;
}
template
static bool Decode2to1(const T* aSrc, U* aDest, Decoder aToVal) {
uint8_t w, x;
if (!aToVal(aSrc[0], &w) || !aToVal(aSrc[1], &x)) {
return false;
}
aDest[0] = U(uint8_t(w << 2 | x >> 4));
return true;
}
template
static nsresult Base64DecodeHelper(const SrcT* aBase64, uint32_t aBase64Len,
DestT* aBinary, uint32_t* aBinaryLen) {
MOZ_ASSERT(aBinary);
const SrcT* input = aBase64;
uint32_t inputLength = aBase64Len;
DestT* binary = aBinary;
uint32_t binaryLength = 0;
// Handle trailing '=' characters.
if (inputLength && (inputLength % 4 == 0)) {
if (aBase64[inputLength - 1] == SrcT('=')) {
if (aBase64[inputLength - 2] == SrcT('=')) {
inputLength -= 2;
} else {
inputLength -= 1;
}
}
}
while (inputLength >= 4) {
if (!Decode4to3(input, binary, Base64CharToValue)) {
return NS_ERROR_INVALID_ARG;
}
input += 4;
inputLength -= 4;
binary += 3;
binaryLength += 3;
}
switch (inputLength) {
case 3:
if (!Decode3to2(input, binary, Base64CharToValue)) {
return NS_ERROR_INVALID_ARG;
}
binaryLength += 2;
break;
case 2:
if (!Decode2to1(input, binary, Base64CharToValue)) {
return NS_ERROR_INVALID_ARG;
}
binaryLength += 1;
break;
case 1:
return NS_ERROR_INVALID_ARG;
case 0:
break;
default:
MOZ_CRASH("Too many characters leftover");
}
aBinary[binaryLength] = DestT('\0');
*aBinaryLen = binaryLength;
return NS_OK;
}
nsresult Base64Decode(const char* aBase64, uint32_t aBase64Len, char** aBinary,
uint32_t* aBinaryLen) {
// Check for overflow.
if (aBase64Len > UINT32_MAX / 3) {
return NS_ERROR_FAILURE;
}
// Don't ask PR_Base64Decode to decode the empty string.
if (aBase64Len == 0) {
*aBinary = (char*)moz_xmalloc(1);
(*aBinary)[0] = '\0';
*aBinaryLen = 0;
return NS_OK;
}
*aBinary = nullptr;
*aBinaryLen = (aBase64Len * 3) / 4;
// Add one byte for null termination.
UniqueFreePtr binary((char*)malloc(*aBinaryLen + 1));
if (!binary) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv =
Base64DecodeHelper(aBase64, aBase64Len, binary.get(), aBinaryLen);
if (NS_FAILED(rv)) {
return rv;
}
*aBinary = binary.release();
return NS_OK;
}
template
static nsresult Base64DecodeString(const T& aBase64, T& aBinary) {
aBinary.Truncate();
// Check for overflow.
if (aBase64.Length() > UINT32_MAX / 3) {
return NS_ERROR_FAILURE;
}
// Don't decode the empty string
if (aBase64.IsEmpty()) {
return NS_OK;
}
uint32_t binaryLen = ((aBase64.Length() * 3) / 4);
auto handleOrErr = aBinary.BulkWrite(binaryLen, 0, false);
if (handleOrErr.isErr()) {
// Must not touch the handle if failing here, but we
// already truncated the string at the top, so it's
// unchanged.
return handleOrErr.unwrapErr();
}
auto handle = handleOrErr.unwrap();
nsresult rv = Base64DecodeHelper(aBase64.BeginReading(), aBase64.Length(),
handle.Elements(), &binaryLen);
if (NS_FAILED(rv)) {
// Retruncate to match old semantics of this method.
handle.Finish(0, true);
return rv;
}
handle.Finish(binaryLen, true);
return NS_OK;
}
nsresult Base64Decode(const nsACString& aBase64, nsACString& aBinary) {
return Base64DecodeString(aBase64, aBinary);
}
nsresult Base64Decode(const nsAString& aBase64, nsAString& aBinary) {
return Base64DecodeString(aBase64, aBinary);
}
nsresult Base64URLDecode(const nsACString& aBase64,
Base64URLDecodePaddingPolicy aPaddingPolicy,
FallibleTArray& aBinary) {
// Don't decode empty strings.
if (aBase64.IsEmpty()) {
aBinary.Clear();
return NS_OK;
}
// Check for overflow.
uint32_t base64Len = aBase64.Length();
if (base64Len > UINT32_MAX / 3) {
return NS_ERROR_FAILURE;
}
const char* base64 = aBase64.BeginReading();
// The decoded length may be 1-2 bytes over, depending on the final quantum.
uint32_t binaryLen = (base64Len * 3) / 4;
// Determine whether to check for and ignore trailing padding.
bool maybePadded = false;
switch (aPaddingPolicy) {
case Base64URLDecodePaddingPolicy::Require:
if (base64Len % 4) {
// Padded input length must be a multiple of 4.
return NS_ERROR_INVALID_ARG;
}
maybePadded = true;
break;
case Base64URLDecodePaddingPolicy::Ignore:
// Check for padding only if the length is a multiple of 4.
maybePadded = !(base64Len % 4);
break;
// If we're expecting unpadded input, no need for additional checks.
// `=` isn't in the decode table, so padded strings will fail to decode.
default:
MOZ_FALLTHROUGH_ASSERT("Invalid decode padding policy");
case Base64URLDecodePaddingPolicy::Reject:
break;
}
if (maybePadded && base64[base64Len - 1] == '=') {
if (base64[base64Len - 2] == '=') {
base64Len -= 2;
} else {
base64Len -= 1;
}
}
if (NS_WARN_IF(!aBinary.SetCapacity(binaryLen, mozilla::fallible))) {
return NS_ERROR_OUT_OF_MEMORY;
}
aBinary.SetLengthAndRetainStorage(binaryLen);
uint8_t* binary = aBinary.Elements();
for (; base64Len >= 4; base64Len -= 4) {
if (!Decode4to3(base64, binary, Base64URLCharToValue)) {
return NS_ERROR_INVALID_ARG;
}
base64 += 4;
binary += 3;
}
if (base64Len == 3) {
if (!Decode3to2(base64, binary, Base64URLCharToValue)) {
return NS_ERROR_INVALID_ARG;
}
binary += 2;
} else if (base64Len == 2) {
if (!Decode2to1(base64, binary, Base64URLCharToValue)) {
return NS_ERROR_INVALID_ARG;
}
binary += 1;
} else if (base64Len) {
return NS_ERROR_INVALID_ARG;
}
// Set the length to the actual number of decoded bytes.
aBinary.TruncateLength(binary - aBinary.Elements());
return NS_OK;
}
nsresult Base64URLEncode(uint32_t aBinaryLen, const uint8_t* aBinary,
Base64URLEncodePaddingPolicy aPaddingPolicy,
nsACString& aBase64) {
aBase64.Truncate();
// Don't encode empty strings.
if (aBinaryLen == 0) {
return NS_OK;
}
// Allocate a buffer large enough to hold the encoded string with padding.
const auto base64LenOrErr = CalculateBase64EncodedLength(aBinaryLen);
if (base64LenOrErr.isErr()) {
return base64LenOrErr.inspectErr();
}
const uint32_t base64Len = base64LenOrErr.inspect();
auto handleOrErr = aBase64.BulkWrite(base64Len, 0, false);
if (handleOrErr.isErr()) {
return handleOrErr.unwrapErr();
}
auto handle = handleOrErr.unwrap();
char* base64 = handle.Elements();
uint32_t index = 0;
for (; index + 3 <= aBinaryLen; index += 3) {
*base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
*base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4) |
(aBinary[index + 1] >> 4)];
*base64++ = kBase64URLAlphabet[((aBinary[index + 1] & 0xf) << 2) |
(aBinary[index + 2] >> 6)];
*base64++ = kBase64URLAlphabet[aBinary[index + 2] & 0x3f];
}
uint32_t remaining = aBinaryLen - index;
if (remaining == 1) {
*base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
*base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4)];
} else if (remaining == 2) {
*base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
*base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4) |
(aBinary[index + 1] >> 4)];
*base64++ = kBase64URLAlphabet[((aBinary[index + 1] & 0xf) << 2)];
}
uint32_t length = base64 - handle.Elements();
if (aPaddingPolicy == Base64URLEncodePaddingPolicy::Include) {
if (length % 4 == 2) {
*base64++ = '=';
*base64++ = '=';
length += 2;
} else if (length % 4 == 3) {
*base64++ = '=';
length += 1;
}
} else {
MOZ_ASSERT(aPaddingPolicy == Base64URLEncodePaddingPolicy::Omit,
"Invalid encode padding policy");
}
handle.Finish(length, false);
return NS_OK;
}
} // namespace mozilla
Minimal test - lines (17, 331)
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 40.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 3.3333333333333335
path: .spaces[0].metrics.nexits.average
old: null
new: 1.4
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 14.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 54.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 108.0
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 18.0
path: .spaces[0].metrics.loc.ploc
old: 4.0
new: 243.0
path: .spaces[0].metrics.loc.sloc
old: 4.0
new: 315.0
path: .spaces[0].metrics.cognitive.average
old: null
new: 3.6
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 36.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.3585392863324195
new: 0.8857908470941424
path: .spaces[0].metrics.halstead.volume
old: 18.094737505048094
new: 12809.303554065222
path: .spaces[0].metrics.halstead.N2
old: 3.0
new: 714.0
path: .spaces[0].metrics.halstead.n1
old: 3.0
new: 46.0
path: .spaces[0].metrics.halstead.n2
old: 3.0
new: 165.0
path: .spaces[0].metrics.halstead.length
old: 7.0
new: 1659.0
path: .spaces[0].metrics.halstead.time
old: 1.5078947920873411
new: 70826.39157065963
path: .spaces[0].metrics.halstead.estimated_program_length
old: 9.509775004326936
new: 1469.5270153291822
path: .spaces[0].metrics.halstead.level
old: 0.6666666666666666
new: 0.010047497259773474
path: .spaces[0].metrics.halstead.vocabulary
old: 6.0
new: 211.0
path: .spaces[0].metrics.halstead.N1
old: 4.0
new: 945.0
path: .spaces[0].metrics.halstead.bugs
old: 0.003010517177243676
new: 3.919137428977254
path: .spaces[0].metrics.halstead.difficulty
old: 1.5
new: 99.52727272727272
path: .spaces[0].metrics.halstead.effort
old: 27.14210625757214
new: 1274875.0482718733
path: .spaces[0].metrics.mi.mi_visual_studio
old: 77.92678442554133
new: 11.360878781036051
path: .spaces[0].metrics.mi.mi_sei
old: 116.64700897518124
new: -13.683702467985016
path: .spaces[0].metrics.mi.mi_original
old: 133.25480136767567
new: 19.42710271557165
path: .spaces[0].metrics.nargs.average
old: null
new: 2.6
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 26.0
path: .spaces[0].metrics.nom.total
old: 0.0
new: 10.0
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 10.0
Code
namespace {
// BEGIN base64 encode code copied and modified from NSPR
const unsigned char* const base =
(unsigned char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
// The Base64 encoder assumes all characters are less than 256; for 16-bit
// strings, that means assuming that all characters are within range, and
// masking off high bits if necessary.
template
uint8_t CharTo8Bit(T aChar) {
return uint8_t(aChar);
}
template
static void Encode3to4(const SrcT* aSrc, DestT* aDest) {
uint32_t b32 = (uint32_t)0;
int i, j = 18;
for (i = 0; i < 3; ++i) {
b32 <<= 8;
b32 |= CharTo8Bit(aSrc[i]);
}
for (i = 0; i < 4; ++i) {
aDest[i] = base[(uint32_t)((b32 >> j) & 0x3F)];
j -= 6;
}
}
template
static void Encode2to4(const SrcT* aSrc, DestT* aDest) {
uint8_t src0 = CharTo8Bit(aSrc[0]);
uint8_t src1 = CharTo8Bit(aSrc[1]);
aDest[0] = base[(uint32_t)((src0 >> 2) & 0x3F)];
aDest[1] = base[(uint32_t)(((src0 & 0x03) << 4) | ((src1 >> 4) & 0x0F))];
aDest[2] = base[(uint32_t)((src1 & 0x0F) << 2)];
aDest[3] = DestT('=');
}
template
static void Encode1to4(const SrcT* aSrc, DestT* aDest) {
uint8_t src0 = CharTo8Bit(aSrc[0]);
aDest[0] = base[(uint32_t)((src0 >> 2) & 0x3F)];
aDest[1] = base[(uint32_t)((src0 & 0x03) << 4)];
aDest[2] = DestT('=');
aDest[3] = DestT('=');
}
template
static void Encode(const SrcT* aSrc, uint32_t aSrcLen, DestT* aDest) {
while (aSrcLen >= 3) {
Encode3to4(aSrc, aDest);
aSrc += 3;
aDest += 4;
aSrcLen -= 3;
}
switch (aSrcLen) {
case 2:
Encode2to4(aSrc, aDest);
break;
case 1:
Encode1to4(aSrc, aDest);
break;
case 0:
break;
default:
MOZ_ASSERT_UNREACHABLE("coding error");
}
}
// END base64 encode code copied and modified from NSPR.
template
struct EncodeInputStream_State {
unsigned char c[3];
uint8_t charsOnStack;
typename T::char_type* buffer;
};
template
nsresult EncodeInputStream_Encoder(nsIInputStream* aStream, void* aClosure,
const char* aFromSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t* aWriteCount) {
MOZ_ASSERT(aCount > 0, "Er, what?");
EncodeInputStream_State* state =
static_cast*>(aClosure);
// We consume the whole data always.
*aWriteCount = aCount;
// If we have any data left from last time, encode it now.
uint32_t countRemaining = aCount;
const unsigned char* src = (const unsigned char*)aFromSegment;
if (state->charsOnStack) {
MOZ_ASSERT(state->charsOnStack == 1 || state->charsOnStack == 2);
// Not enough data to compose a triple.
if (state->charsOnStack == 1 && countRemaining == 1) {
state->charsOnStack = 2;
state->c[1] = src[0];
return NS_OK;
}
uint32_t consumed = 0;
unsigned char firstSet[4];
if (state->charsOnStack == 1) {
firstSet[0] = state->c[0];
firstSet[1] = src[0];
firstSet[2] = src[1];
firstSet[3] = '\0';
consumed = 2;
} else /* state->charsOnStack == 2 */ {
firstSet[0] = state->c[0];
firstSet[1] = state->c[1];
firstSet[2] = src[0];
firstSet[3] = '\0';
consumed = 1;
}
Encode(firstSet, 3, state->buffer);
state->buffer += 4;
countRemaining -= consumed;
src += consumed;
state->charsOnStack = 0;
// Nothing is left.
if (!countRemaining) {
return NS_OK;
}
}
// Encode as many full triplets as possible.
uint32_t encodeLength = countRemaining - countRemaining % 3;
MOZ_ASSERT(encodeLength % 3 == 0, "Should have an exact number of triplets!");
Encode(src, encodeLength, state->buffer);
state->buffer += (encodeLength / 3) * 4;
src += encodeLength;
countRemaining -= encodeLength;
if (countRemaining) {
// We should never have a full triplet left at this point.
MOZ_ASSERT(countRemaining < 3, "We should have encoded more!");
state->c[0] = src[0];
state->c[1] = (countRemaining == 2) ? src[1] : '\0';
state->charsOnStack = countRemaining;
}
return NS_OK;
}
mozilla::Result CalculateBase64EncodedLength(
const size_t aBinaryLen, const uint32_t aPrefixLen = 0) {
mozilla::CheckedUint32 res = aBinaryLen;
// base 64 encoded length is 4/3rds the length of the input data, rounded up
res += 2;
res /= 3;
res *= 4;
res += aPrefixLen;
if (!res.isValid()) {
return mozilla::Err(NS_ERROR_FAILURE);
}
return res.value();
}
template
nsresult EncodeInputStream(nsIInputStream* aInputStream, T& aDest,
uint32_t aCount, uint32_t aOffset) {
nsresult rv;
uint64_t count64 = aCount;
if (!aCount) {
rv = aInputStream->Available(&count64);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// if count64 is over 4GB, it will be failed at the below condition,
// then will return NS_ERROR_OUT_OF_MEMORY
aCount = (uint32_t)count64;
}
const auto base64LenOrErr = CalculateBase64EncodedLength(count64, aOffset);
if (base64LenOrErr.isErr()) {
// XXX For some reason, it was NS_ERROR_OUT_OF_MEMORY here instead of
// NS_ERROR_FAILURE, so we keep that.
return NS_ERROR_OUT_OF_MEMORY;
}
if (!aDest.SetLength(base64LenOrErr.inspect(), mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
EncodeInputStream_State state;
state.charsOnStack = 0;
state.c[2] = '\0';
state.buffer = aOffset + aDest.BeginWriting();
while (true) {
uint32_t read = 0;
rv = aInputStream->ReadSegments(&EncodeInputStream_Encoder,
(void*)&state, aCount, &read);
if (NS_FAILED(rv)) {
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
MOZ_CRASH("Not implemented for async streams!");
}
if (rv == NS_ERROR_NOT_IMPLEMENTED) {
MOZ_CRASH("Requires a stream that implements ReadSegments!");
}
return rv;
}
if (!read) {
break;
}
}
// Finish encoding if anything is left
if (state.charsOnStack) {
Encode(state.c, state.charsOnStack, state.buffer);
}
if (aDest.Length()) {
// May belong to an nsCString with an unallocated buffer, so only null
// terminate if there is a need to.
*aDest.EndWriting() = '\0';
}
return NS_OK;
}
// Maps an encoded character to a value in the Base64 alphabet, per
// RFC 4648, Table 1. Invalid input characters map to UINT8_MAX.
static const uint8_t kBase64DecodeTable[] = {
// clang-format off
/* 0 */ 255, 255, 255, 255, 255, 255, 255, 255,
/* 8 */ 255, 255, 255, 255, 255, 255, 255, 255,
/* 16 */ 255, 255, 255, 255, 255, 255, 255, 255,
/* 24 */ 255, 255, 255, 255, 255, 255, 255, 255,
/* 32 */ 255, 255, 255, 255, 255, 255, 255, 255,
/* 40 */ 255, 255, 255,
62 /* + */,
255, 255, 255,
63 /* / */,
/* 48 */ /* 0 - 9 */ 52, 53, 54, 55, 56, 57, 58, 59,
/* 56 */ 60, 61, 255, 255, 255, 255, 255, 255,
/* 64 */ 255, /* A - Z */ 0, 1, 2, 3, 4, 5, 6,
/* 72 */ 7, 8, 9, 10, 11, 12, 13, 14,
/* 80 */ 15, 16, 17, 18, 19, 20, 21, 22,
/* 88 */ 23, 24, 25, 255, 255, 255, 255, 255,
/* 96 */ 255, /* a - z */ 26, 27, 28, 29, 30, 31, 32,
/* 104 */ 33, 34, 35, 36, 37, 38, 39, 40,
/* 112 */ 41, 42, 43, 44, 45, 46, 47, 48,
/* 120 */ 49, 50, 51, 255, 255, 255, 255, 255,
};
// clang-format on
template
[[nodiscard]] bool Base64CharToValue(T aChar, uint8_t* aValue) {
static const size_t mask = 0x7f;
static_assert(
(mask + 1) == sizeof(kBase64DecodeTable) / sizeof(kBase64DecodeTable[0]),
"wrong mask");
size_t index = static_cast(aChar);
if (index & ~mask) {
return false;
}
*aValue = kBase64DecodeTable[index & mask];
return *aValue != 255;
}
static const char kBase64URLAlphabet[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
// Maps an encoded character to a value in the Base64 URL alphabet, per
// RFC 4648, Table 2. Invalid input characters map to UINT8_MAX.
static const uint8_t kBase64URLDecodeTable[] = {
// clang-format off
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255,
62 /* - */,
255, 255,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* 0 - 9 */
255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* A - Z */
255, 255, 255, 255,
63 /* _ */,
255,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* a - z */
255, 255, 255, 255,
};
// clang-format on
bool Base64URLCharToValue(char aChar, uint8_t* aValue) {
uint8_t index = static_cast(aChar);
*aValue = kBase64URLDecodeTable[index & 0x7f];
return (*aValue != 255) && !(index & ~0x7f);
}
} // namespace