Files
Benjamin Beurdouche 7c4bfd7c72 Bug 1996558 - Add shared mozilla::HexEncode/HexDecode helpers in xpcom/io r=xpcom-reviewers,emilio
Several components hand-roll a byte<->hex loop; gcp flagged (D301076) that the SQLite-encryption keystore was about to add yet another copy. Introduce shared mozilla::HexEncode (with an optional uppercase mode) and its inverse mozilla::HexDecode in xpcom/io, next to Base64Encode/Base64Decode, with gtests. mfbt would be the natural home but cannot depend on nsACString, so the helpers live in the XPCOM io/ module.

Differential Revision: https://phabricator.services.mozilla.com/D303706
2026-06-16 13:00:16 +00:00

52 lines
1.6 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Hex.h"
#include "mozilla/TextUtils.h"
#include "nsError.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
void HexEncode(Span<const uint8_t> aBytes, nsACString& aOut, bool aUpperCase) {
static const char kLowerHexChars[] = "0123456789abcdef";
static const char kUpperHexChars[] = "0123456789ABCDEF";
const char* const hexChars = aUpperCase ? kUpperHexChars : kLowerHexChars;
aOut.SetLength(aBytes.Length() * 2);
char* out = aOut.BeginWriting();
for (uint8_t byte : aBytes) {
*out++ = hexChars[(byte >> 4) & 0xF];
*out++ = hexChars[byte & 0xF];
}
}
nsresult HexDecode(const nsACString& aHex, nsTArray<uint8_t>& aOut) {
aOut.Clear();
const size_t length = aHex.Length();
if (length % 2 != 0) {
return NS_ERROR_INVALID_ARG;
}
// Pre-reserve the exact output size (fallible) so the per-byte appends below
// never reallocate and an oversized input fails cleanly on OOM.
if (!aOut.SetCapacity(length / 2, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
const char* const hex = aHex.BeginReading();
for (size_t i = 0; i < length; i += 2) {
const char hi = hex[i];
const char lo = hex[i + 1];
if (!IsAsciiHexDigit(hi) || !IsAsciiHexDigit(lo)) {
aOut.Clear();
return NS_ERROR_INVALID_ARG;
}
aOut.AppendElement(static_cast<uint8_t>(
(AsciiAlphanumericToNumber(hi) << 4) | AsciiAlphanumericToNumber(lo)));
}
return NS_OK;
}
} // namespace mozilla