3
// Copyright (C) 2007-present, Celestia Development Team
4
// Original version by Chris Laurel <claurel@shatters.net>
6
// 128-bit fixed point (64.64) numbers for high-precision celestial
7
// coordinates. When you need millimeter accurate navigation across a scale
8
// of thousands of light years, double precision floating point numbers
11
// This program is free software; you can redistribute it and/or
12
// modify it under the terms of the GNU General Public License
13
// as published by the Free Software Foundation; either version 2
14
// of the License, or (at your option) any later version.
22
#define R128_IMPLEMENTATION
25
using namespace std::string_view_literals;
30
constexpr std::string_view alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"sv;
31
static_assert(alphabet.size() == 64, "Base64 decoder alphabet must be of length 64");
33
constexpr std::uint8_t AsciiRange = 128;
35
using DecoderArray = std::array<std::int8_t, AsciiRange>;
37
DecoderArray createBase64Decoder()
41
for (std::size_t i = 0; i < alphabet.size(); ++i)
43
std::uint8_t idx = static_cast<std::uint8_t>(alphabet[i]);
44
assert(idx < AsciiRange);
45
decoder[idx] = static_cast<std::int8_t>(i);
51
} // end unnamed namespace
53
namespace celestia::util
56
std::string EncodeAsBase64(const R128 &b)
58
// Old BigFix class used 8 16-bit words. The bulk of this function
59
// is copied from that class, so first we'll convert from two
60
// 64-bit words to 8 16-bit words so that the old code can work
62
std::array<std::uint16_t, 8> n =
64
static_cast<std::uint16_t>(b.lo),
65
static_cast<std::uint16_t>(b.lo >> 16),
66
static_cast<std::uint16_t>(b.lo >> 32),
67
static_cast<std::uint16_t>(b.lo >> 48),
69
static_cast<std::uint16_t>(b.hi),
70
static_cast<std::uint16_t>(b.hi >> 16),
71
static_cast<std::uint16_t>(b.hi >> 32),
72
static_cast<std::uint16_t>(b.hi >> 48),
75
// Conversion using code from the original R128 class.
77
int bits, c, char_count, i;
82
// Find first significant (non null) byte
87
if ((i & 1) != 0) c >>= 8;
89
} while ((c == 0) && (i != 0));
94
// Then we encode starting by the LSB (i+1 bytes to encode)
95
for (auto j = 0; j <= i; j++)
98
if ( (j & 1) != 0 ) c >>= 8;
104
encoded += alphabet[bits >> 18];
105
encoded += alphabet[(bits >> 12) & 0x3f];
106
encoded += alphabet[(bits >> 6) & 0x3f];
107
encoded += alphabet[bits & 0x3f];
119
bits <<= 16 - (8 * char_count);
120
encoded += alphabet[bits >> 18];
121
encoded += alphabet[(bits >> 12) & 0x3f];
123
encoded += alphabet[(bits >> 6) & 0x3f];
130
R128 DecodeFromBase64(std::string_view val)
132
static DecoderArray decoder = createBase64Decoder();
134
std::array<std::uint16_t, 8> n = {0};
136
// Code from original BigFix class to convert base64 string into
137
// array of 8 16-bit values.
143
for (unsigned char c : val)
149
std::int8_t decoded = decoder[c];
157
n[i/2] += (bits >> 8) & 0xff00;
160
n[i/2] += bits & 0xff00;
163
n[i/2] += (bits << 8) & 0xff00;
178
n[i/2] += (bits >> 2) & 0xff00;
183
n[i/2] += (bits >> 8) & 0xff00;
186
n[i/2] += bits & 0xff00;
194
// Now, convert the 8 16-bit values to a 2 64-bit values
195
std::uint64_t lo = (static_cast<std::uint64_t>(n[0])
196
| (static_cast<std::uint64_t>(n[1]) << 16)
197
| (static_cast<std::uint64_t>(n[2]) << 32)
198
| (static_cast<std::uint64_t>(n[3]) << 48));
199
std::uint64_t hi = (static_cast<std::uint64_t>(n[4])
200
| (static_cast<std::uint64_t>(n[5]) << 16)
201
| (static_cast<std::uint64_t>(n[6]) << 32)
202
| (static_cast<std::uint64_t>(n[7]) << 48));
207
bool isOutOfBounds(const R128 &b)
209
constexpr std::uint64_t hi_threshold = UINT64_C(1) << 62;
210
constexpr std::uint64_t lo_threshold = static_cast<std::uint64_t>(-static_cast<std::int64_t>(hi_threshold));
211
return (b.hi > hi_threshold && b.hi < lo_threshold);
214
} // end namespace celestia::util