blob: 6f2382e23d50ea748a70b3dc12d4744e9ad0028a [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/storage/volume_image/utils/guid.h"
#include <zircon/assert.h>
#include <algorithm>
#include <array>
#include <cstdint>
#include <iterator>
#include <string>
#include <safemath/safe_conversions.h>
namespace storage::volume_image {
namespace {
// GuidSection represents a sequence of bytes within a representation of a GUID,
// that treated as a unit. This unit may be represented as little/big endian
// independently of the platform.
//
// Offsets and lengths are defined with respect to the byte sequence.
struct GuidSection {
// Returns the offset of the first element in the section.
constexpr ptrdiff_t begin() const {
return static_cast<ptrdiff_t>(multiplier) * (reversed ? start + length - 1 : start);
}
// Returns the offset of the last element in the section.
constexpr ptrdiff_t end() const {
return static_cast<ptrdiff_t>(multiplier) * (reversed ? start - 1 : start + length);
}
// Returns the distance between two consecutive elements in the section, measured in bytes.
constexpr ptrdiff_t next() const {
return static_cast<ptrdiff_t>(multiplier) * (reversed ? -1 : +1);
}
// Start of the section.
uint8_t start;
// End of the section.
uint8_t length;
// Whether this section should be iterated in a particular order.
bool reversed;
// Byte size of elements.
uint8_t multiplier = 1;
};
// Defines the different sections of the GUID to match the following format:
//
// Byte-Format: {section_0}....{section_N}
// String Format: {String(section_0)}-....-{String(section_N)}
//
// Example:
// Byte-Format: {0xA0, 0xA1, 0xA2, 0xA3, 0xB0, 0xB1, 0xC0, 0xC1, 0xD0, 0xD1, 0xE0, 0xE1, 0xE2,
// 0xE3, 0xE4, 0xE5}
// String-Format: A3A2A1A0-B1B0-C1C0-D0D1-E0E1E2E3E4E5
constexpr std::array<GuidSection, 5> kGuidSections = {
{
// section: 0
// Bytes: {0xA0, 0xA1, 0xA2, 0xA3}
// String: A3A2A1A0
{.start = 0, .length = 4, .reversed = true},
// section: 1
// Bytes: {0xB0, 0xB1}
// String: B1B0
{4, 2, true},
// section: 2
// Bytes: {0xC0, 0xC1}
// String C1C0
{6, 2, true},
// section: 3
// Bytes: {0xD0, 0xD1}
// D0D1
{8, 2, false},
// section 4
// Bytes: {0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5}
// String E0E1E2E3E4E5
{10, 6, false},
},
};
// Separator used for string representation of the GUID.
constexpr char kSeparator = '-';
// Returns the Hex equivalent of |value|.
//
// Precondition:
// * |value| is less than 16 or |F| + 1.
constexpr char GetHex(uint8_t value) {
ZX_ASSERT(value < 16);
constexpr std::string_view kHexTable = "0123456789ABCDEF";
return kHexTable[value];
}
// Returns the numeric value of |hex|.
//
// Precondition:
// * |hex| is either a digit(0-9) or [a-fA-F].
uint8_t GetValue(char hex) {
constexpr uint8_t kHexAlphaOffset = 0xA;
if (hex >= 'a') {
return hex - 'a' + kHexAlphaOffset;
}
if (hex >= 'A') {
return hex - 'A' + kHexAlphaOffset;
}
return hex - '0';
}
constexpr uint64_t kHighMask = 0xF0;
constexpr uint64_t kLowMask = 0x0F;
} // namespace
fpromise::result<std::string, std::string> Guid::ToString(cpp20::span<const uint8_t> guid) {
if (guid.size() != kGuidLength) {
std::string error = "Input GUID size must be equal to |kGuidLength|. Input Size: ";
error.append(std::to_string(guid.size())).append(".\n");
return fpromise::error(error);
}
std::array<char, kGuidStrLength> out_guid;
uint8_t current_section = 0;
uint8_t current_byte = 0;
for (auto section : kGuidSections) {
section.multiplier = sizeof(uint8_t);
const uint8_t* begin = guid.data() + section.begin();
const uint8_t* end = guid.data() + section.end();
for (const uint8_t* it = begin; it != end; it = it + section.next()) {
uint8_t high = (kHighMask & *it) >> 4;
uint8_t low = (kLowMask & *it);
out_guid[2 * current_byte + current_section] = GetHex(high);
out_guid[2 * current_byte + current_section + 1] = GetHex(low);
current_byte++;
}
// We dont need a separator after last section.
if (current_section < kGuidSections.size() - 1) {
out_guid[2 * (section.start + section.length) + current_section] = kSeparator;
}
current_section++;
}
return fpromise::ok(std::string(out_guid.data(), out_guid.size()));
}
fpromise::result<std::array<uint8_t, kGuidLength>, std::string> Guid::FromString(
cpp20::span<const char> guid) {
if (guid.size() != kGuidStrLength) {
std::string error = "Input GUID size must be equal to |kGuidStrLength|. Input Size: ";
error.append(std::to_string(guid.size())).append(".\n");
return fpromise::error(error);
}
std::array<uint8_t, kGuidLength> out_guid;
uint64_t current_byte = 0;
uint64_t current_section = 0;
for (auto section : kGuidSections) {
// We iterate 2 characters at a time.
section.multiplier = kGuidCharactersPerByte * sizeof(char);
const char* begin = guid.data() + section.begin() + current_section;
const char* end = guid.data() + section.end() + current_section;
for (const char* it = begin; it != end; it = it + section.next()) {
uint8_t high = GetValue(*it);
uint8_t low = GetValue(*(it + 1));
out_guid[current_byte] = safemath::checked_cast<uint8_t>((high << 4) | low);
current_byte++;
}
current_section++;
}
return fpromise::ok(out_guid);
}
} // namespace storage::volume_image