mirror of
https://github.com/isar/libmdbx.git
synced 2024-12-30 02:24:14 +08:00
mdbx++: переделка поддержки base58 по RFC-draft.
This commit is contained in:
parent
dd47f1bfd9
commit
be8428257d
5
mdbx.h++
5
mdbx.h++
@ -1362,8 +1362,7 @@ struct LIBMDBX_API to_base58 {
|
||||
/// \brief Returns the buffer size in bytes needed for
|
||||
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump of passed slice.
|
||||
MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
|
||||
const size_t bytes =
|
||||
source.length() / 8 * 11 + (source.length() % 8 * 43 + 31) / 32;
|
||||
const size_t bytes = (source.length() * 11 + 7) / 8;
|
||||
return wrap_width ? bytes + bytes / wrap_width : bytes;
|
||||
}
|
||||
|
||||
@ -1528,7 +1527,7 @@ struct LIBMDBX_API from_base58 {
|
||||
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice to
|
||||
/// decoded data.
|
||||
MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
|
||||
return source.length() / 11 * 8 + source.length() % 11 * 32 / 43;
|
||||
return source.length() /* могут быть все нули кодируемые один-к-одному */;
|
||||
}
|
||||
|
||||
/// \brief Fills the destination with data decoded from
|
||||
|
416
src/mdbx.c++
416
src/mdbx.c++
@ -207,6 +207,44 @@ __cold bug::~bug() noexcept {}
|
||||
|
||||
#endif /* Unused*/
|
||||
|
||||
struct line_wrapper {
|
||||
char *line, *ptr;
|
||||
line_wrapper(char *buf) noexcept : line(buf), ptr(buf) {}
|
||||
void put(char c, size_t wrap_width) noexcept {
|
||||
*ptr++ = c;
|
||||
if (wrap_width && ptr >= wrap_width + line) {
|
||||
*ptr++ = '\n';
|
||||
line = ptr;
|
||||
}
|
||||
}
|
||||
void put(const ::mdbx::slice &chunk, size_t wrap_width) noexcept {
|
||||
if (!wrap_width || wrap_width > (ptr - line) + chunk.length()) {
|
||||
memcpy(ptr, chunk.data(), chunk.length());
|
||||
ptr += chunk.length();
|
||||
} else {
|
||||
for (size_t i = 0; i < chunk.length(); ++i)
|
||||
put(chunk.char_ptr()[i], wrap_width);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TYPE, unsigned INPLACE_BYTES = unsigned(sizeof(void *) * 64)>
|
||||
struct temp_buffer {
|
||||
TYPE inplace[(INPLACE_BYTES + sizeof(TYPE) - 1) / sizeof(TYPE)];
|
||||
const size_t size;
|
||||
TYPE *const area;
|
||||
temp_buffer(size_t bytes)
|
||||
: size((bytes + sizeof(TYPE) - 1) / sizeof(TYPE)),
|
||||
area((bytes > sizeof(inplace)) ? new TYPE[size] : inplace) {
|
||||
memset(area, 0, sizeof(TYPE) * size);
|
||||
}
|
||||
~temp_buffer() {
|
||||
if (area != inplace)
|
||||
delete[] area;
|
||||
}
|
||||
TYPE *end() const { return area + size; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -717,156 +755,135 @@ enum : signed char {
|
||||
IL /* invalid */ = -1
|
||||
};
|
||||
|
||||
static const byte b58_alphabet[58] = {
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm',
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
|
||||
|
||||
#ifndef bswap64
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
static inline uint64_t bswap64(uint64_t v) noexcept {
|
||||
#if __GNUC_PREREQ(4, 4) || __CLANG_PREREQ(4, 0) || \
|
||||
__has_builtin(__builtin_bswap64)
|
||||
return __builtin_bswap64(v);
|
||||
#elif defined(_MSC_VER) && !defined(__clang__)
|
||||
return _byteswap_uint64(v);
|
||||
#elif defined(__bswap_64)
|
||||
return __bswap_64(v);
|
||||
#elif defined(bswap_64)
|
||||
return bswap_64(v);
|
||||
#if MDBX_WORDBITS > 32
|
||||
using b58_uint = uint_fast64_t;
|
||||
#else
|
||||
return v << 56 | v >> 56 | ((v << 40) & UINT64_C(0x00ff000000000000)) |
|
||||
((v << 24) & UINT64_C(0x0000ff0000000000)) |
|
||||
((v << 8) & UINT64_C(0x000000ff00000000)) |
|
||||
((v >> 8) & UINT64_C(0x00000000ff000000)) |
|
||||
((v >> 24) & UINT64_C(0x0000000000ff0000)) |
|
||||
((v >> 40) & UINT64_C(0x000000000000ff00));
|
||||
using b58_uint = uint_fast32_t;
|
||||
#endif
|
||||
}
|
||||
#endif /* __BYTE_ORDER__ */
|
||||
#endif /* ifndef bswap64 */
|
||||
|
||||
static inline char b58_8to11(uint64_t &v) noexcept {
|
||||
const unsigned i = unsigned(v % 58);
|
||||
struct b58_buffer : public temp_buffer<b58_uint> {
|
||||
b58_buffer(size_t bytes, size_t estimation_ratio_numerator,
|
||||
size_t estimation_ratio_denominator, size_t extra = 0)
|
||||
: temp_buffer((/* пересчитываем по указанной пропорции */
|
||||
bytes = (bytes * estimation_ratio_numerator +
|
||||
estimation_ratio_denominator - 1) /
|
||||
estimation_ratio_denominator,
|
||||
/* учитываем резервный старший байт в каждом слове */
|
||||
((bytes + sizeof(b58_uint) - 2) / (sizeof(b58_uint) - 1) *
|
||||
sizeof(b58_uint) +
|
||||
extra) *
|
||||
sizeof(b58_uint))) {}
|
||||
};
|
||||
|
||||
static byte b58_8to11(b58_uint &v) noexcept {
|
||||
static const char b58_alphabet[58] = {
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm',
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
|
||||
|
||||
const auto i = size_t(v % 58);
|
||||
v /= 58;
|
||||
return b58_alphabet[i];
|
||||
}
|
||||
|
||||
static slice b58_encode(b58_buffer &buf, const byte *begin, const byte *end) {
|
||||
auto high = buf.end();
|
||||
const auto modulo =
|
||||
b58_uint((sizeof(b58_uint) > 4) ? UINT64_C(0x1A636A90B07A00) /* 58^9 */
|
||||
: UINT32_C(0xACAD10) /* 58^4 */);
|
||||
static_assert(sizeof(modulo) == 4 || sizeof(modulo) == 8, "WTF?");
|
||||
while (begin < end) {
|
||||
b58_uint carry = *begin++;
|
||||
auto ptr = buf.end();
|
||||
do {
|
||||
assert(ptr > buf.area);
|
||||
carry += *--ptr << CHAR_BIT;
|
||||
*ptr = carry % modulo;
|
||||
carry /= modulo;
|
||||
} while (carry || ptr > high);
|
||||
high = ptr;
|
||||
}
|
||||
|
||||
byte *output = static_cast<byte *>(static_cast<void *>(buf.area));
|
||||
auto ptr = output;
|
||||
for (auto porous = high; porous < buf.end();) {
|
||||
auto chunk = *porous++;
|
||||
static_assert(sizeof(chunk) == 4 || sizeof(chunk) == 8, "WTF?");
|
||||
assert(chunk < modulo);
|
||||
if (sizeof(chunk) > 4) {
|
||||
ptr[8] = b58_8to11(chunk);
|
||||
ptr[7] = b58_8to11(chunk);
|
||||
ptr[6] = b58_8to11(chunk);
|
||||
ptr[5] = b58_8to11(chunk);
|
||||
ptr[4] = b58_8to11(chunk);
|
||||
ptr[3] = b58_8to11(chunk);
|
||||
ptr[2] = b58_8to11(chunk);
|
||||
ptr[1] = b58_8to11(chunk);
|
||||
ptr[0] = b58_8to11(chunk);
|
||||
ptr += 9;
|
||||
} else {
|
||||
ptr[3] = b58_8to11(chunk);
|
||||
ptr[2] = b58_8to11(chunk);
|
||||
ptr[1] = b58_8to11(chunk);
|
||||
ptr[0] = b58_8to11(chunk);
|
||||
ptr += 4;
|
||||
}
|
||||
assert(static_cast<void *>(ptr) < static_cast<void *>(porous));
|
||||
}
|
||||
|
||||
while (output < ptr && *output == '1')
|
||||
++output;
|
||||
return slice(output, ptr);
|
||||
}
|
||||
|
||||
char *to_base58::write_bytes(char *__restrict const dest,
|
||||
size_t dest_size) const {
|
||||
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
|
||||
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
|
||||
|
||||
auto ptr = dest;
|
||||
auto src = source.byte_ptr();
|
||||
size_t left = source.length();
|
||||
auto line = ptr;
|
||||
while (MDBX_LIKELY(left > 7)) {
|
||||
uint64_t v;
|
||||
std::memcpy(&v, src, 8);
|
||||
src += 8;
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
v = bswap64(v);
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#else
|
||||
#error "FIXME: Unsupported byte order"
|
||||
#endif /* __BYTE_ORDER__ */
|
||||
ptr[10] = b58_8to11(v);
|
||||
ptr[9] = b58_8to11(v);
|
||||
ptr[8] = b58_8to11(v);
|
||||
ptr[7] = b58_8to11(v);
|
||||
ptr[6] = b58_8to11(v);
|
||||
ptr[5] = b58_8to11(v);
|
||||
ptr[4] = b58_8to11(v);
|
||||
ptr[3] = b58_8to11(v);
|
||||
ptr[2] = b58_8to11(v);
|
||||
ptr[1] = b58_8to11(v);
|
||||
ptr[0] = b58_8to11(v);
|
||||
assert(v == 0);
|
||||
ptr += 11;
|
||||
left -= 8;
|
||||
if (wrap_width && size_t(ptr - line) >= wrap_width && left) {
|
||||
*ptr = '\n';
|
||||
line = ++ptr;
|
||||
}
|
||||
assert(ptr <= dest + dest_size);
|
||||
auto begin = source.byte_ptr();
|
||||
auto end = source.end_byte_ptr();
|
||||
line_wrapper wrapper(dest);
|
||||
while (MDBX_LIKELY(begin < end) && *begin == 0) {
|
||||
wrapper.put('1', wrap_width);
|
||||
assert(wrapper.ptr <= dest + dest_size);
|
||||
++begin;
|
||||
}
|
||||
|
||||
if (left) {
|
||||
uint64_t v = 0;
|
||||
unsigned parrots = 31;
|
||||
do {
|
||||
v = (v << 8) + *src++;
|
||||
parrots += 43;
|
||||
} while (--left);
|
||||
|
||||
auto tail = ptr += parrots >> 5;
|
||||
assert(ptr <= dest + dest_size);
|
||||
do {
|
||||
*--tail = b58_8to11(v);
|
||||
parrots -= 32;
|
||||
} while (parrots > 31);
|
||||
assert(v == 0);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
b58_buffer buf(end - begin, 11, 8);
|
||||
wrapper.put(b58_encode(buf, begin, end), wrap_width);
|
||||
return wrapper.ptr;
|
||||
}
|
||||
|
||||
::std::ostream &to_base58::output(::std::ostream &out) const {
|
||||
if (MDBX_LIKELY(!is_empty()))
|
||||
MDBX_CXX20_LIKELY {
|
||||
::std::ostream::sentry sentry(out);
|
||||
auto src = source.byte_ptr();
|
||||
size_t left = source.length();
|
||||
auto begin = source.byte_ptr();
|
||||
auto end = source.end_byte_ptr();
|
||||
unsigned width = 0;
|
||||
std::array<char, 11> buf;
|
||||
|
||||
while (MDBX_LIKELY(left > 7)) {
|
||||
uint64_t v;
|
||||
std::memcpy(&v, src, 8);
|
||||
src += 8;
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
v = bswap64(v);
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#else
|
||||
#error "FIXME: Unsupported byte order"
|
||||
#endif /* __BYTE_ORDER__ */
|
||||
buf[10] = b58_8to11(v);
|
||||
buf[9] = b58_8to11(v);
|
||||
buf[8] = b58_8to11(v);
|
||||
buf[7] = b58_8to11(v);
|
||||
buf[6] = b58_8to11(v);
|
||||
buf[5] = b58_8to11(v);
|
||||
buf[4] = b58_8to11(v);
|
||||
buf[3] = b58_8to11(v);
|
||||
buf[2] = b58_8to11(v);
|
||||
buf[1] = b58_8to11(v);
|
||||
buf[0] = b58_8to11(v);
|
||||
assert(v == 0);
|
||||
out.write(&buf.front(), 11);
|
||||
left -= 8;
|
||||
if (wrap_width && (width += 11) >= wrap_width && left) {
|
||||
while (MDBX_LIKELY(begin < end) && *begin == 0) {
|
||||
out.put('1');
|
||||
if (wrap_width && ++width >= wrap_width) {
|
||||
out << ::std::endl;
|
||||
width = 0;
|
||||
}
|
||||
++begin;
|
||||
}
|
||||
|
||||
if (left) {
|
||||
uint64_t v = 0;
|
||||
unsigned parrots = 31;
|
||||
do {
|
||||
v = (v << 8) + *src++;
|
||||
parrots += 43;
|
||||
} while (--left);
|
||||
|
||||
auto ptr = buf.end();
|
||||
do {
|
||||
*--ptr = b58_8to11(v);
|
||||
parrots -= 32;
|
||||
} while (parrots > 31);
|
||||
assert(v == 0);
|
||||
out.write(&*ptr, buf.end() - ptr);
|
||||
b58_buffer buf(end - begin, 11, 8);
|
||||
const auto chunk = b58_encode(buf, begin, end);
|
||||
if (!wrap_width || wrap_width > width + chunk.length())
|
||||
out.write(chunk.char_ptr(), chunk.length());
|
||||
else {
|
||||
for (size_t i = 0; i < chunk.length(); ++i) {
|
||||
out.put(chunk.char_ptr()[i]);
|
||||
if (wrap_width && ++width >= wrap_width) {
|
||||
out << ::std::endl;
|
||||
width = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
@ -892,10 +909,46 @@ const signed char b58_map[256] = {
|
||||
IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL // f0
|
||||
};
|
||||
|
||||
static inline signed char b58_11to8(uint64_t &v, const byte c) noexcept {
|
||||
const signed char m = b58_map[c];
|
||||
v = v * 58 + m;
|
||||
return m;
|
||||
static slice b58_decode(b58_buffer &buf, const byte *begin, const byte *end,
|
||||
bool ignore_spaces) {
|
||||
auto high = buf.end();
|
||||
while (begin < end) {
|
||||
const auto c = b58_map[*begin++];
|
||||
if (MDBX_LIKELY(c >= 0)) {
|
||||
b58_uint carry = c;
|
||||
auto ptr = buf.end();
|
||||
do {
|
||||
assert(ptr > buf.area);
|
||||
carry += *--ptr * 58;
|
||||
*ptr = carry & (~b58_uint(0) >> CHAR_BIT);
|
||||
carry >>= CHAR_BIT * (sizeof(carry) - 1);
|
||||
} while (carry || ptr > high);
|
||||
high = ptr;
|
||||
} else if (MDBX_UNLIKELY(!ignore_spaces || !isspace(begin[-1])))
|
||||
MDBX_CXX20_UNLIKELY
|
||||
throw std::domain_error("mdbx::from_base58:: invalid base58 string");
|
||||
}
|
||||
|
||||
byte *output = static_cast<byte *>(static_cast<void *>(buf.area));
|
||||
auto ptr = output;
|
||||
for (auto porous = high; porous < buf.end(); ++porous) {
|
||||
auto chunk = *porous;
|
||||
static_assert(sizeof(chunk) == 4 || sizeof(chunk) == 8, "WTF?");
|
||||
assert(chunk <= (~b58_uint(0) >> CHAR_BIT));
|
||||
if (sizeof(chunk) > 4) {
|
||||
*ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 6);
|
||||
*ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 5);
|
||||
*ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 4);
|
||||
*ptr++ = byte(chunk >> CHAR_BIT * 3);
|
||||
}
|
||||
*ptr++ = byte(chunk >> CHAR_BIT * 2);
|
||||
*ptr++ = byte(chunk >> CHAR_BIT * 1);
|
||||
*ptr++ = byte(chunk >> CHAR_BIT * 0);
|
||||
}
|
||||
|
||||
while (output < ptr && *output == 0)
|
||||
++output;
|
||||
return slice(output, ptr);
|
||||
}
|
||||
|
||||
char *from_base58::write_bytes(char *__restrict const dest,
|
||||
@ -904,98 +957,33 @@ char *from_base58::write_bytes(char *__restrict const dest,
|
||||
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
|
||||
|
||||
auto ptr = dest;
|
||||
auto src = source.byte_ptr();
|
||||
for (auto left = source.length(); left > 0;) {
|
||||
if (MDBX_UNLIKELY(isspace(*src)) && ignore_spaces) {
|
||||
++src;
|
||||
--left;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MDBX_LIKELY(left > 10)) {
|
||||
uint64_t v = 0;
|
||||
if (MDBX_UNLIKELY((b58_11to8(v, src[0]) | b58_11to8(v, src[1]) |
|
||||
b58_11to8(v, src[2]) | b58_11to8(v, src[3]) |
|
||||
b58_11to8(v, src[4]) | b58_11to8(v, src[5]) |
|
||||
b58_11to8(v, src[6]) | b58_11to8(v, src[7]) |
|
||||
b58_11to8(v, src[8]) | b58_11to8(v, src[9]) |
|
||||
b58_11to8(v, src[10])) < 0))
|
||||
MDBX_CXX20_UNLIKELY goto bailout;
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
v = bswap64(v);
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#else
|
||||
#error "FIXME: Unsupported byte order"
|
||||
#endif /* __BYTE_ORDER__ */
|
||||
std::memcpy(ptr, &v, 8);
|
||||
ptr += 8;
|
||||
src += 11;
|
||||
left -= 11;
|
||||
assert(ptr <= dest + dest_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
constexpr unsigned invalid_length_mask = 1 << 1 | 1 << 4 | 1 << 8;
|
||||
if (MDBX_UNLIKELY(invalid_length_mask & (1 << left)))
|
||||
MDBX_CXX20_UNLIKELY goto bailout;
|
||||
|
||||
uint64_t v = 1;
|
||||
unsigned parrots = 0;
|
||||
do {
|
||||
if (MDBX_UNLIKELY(b58_11to8(v, *src++) < 0))
|
||||
MDBX_CXX20_UNLIKELY goto bailout;
|
||||
parrots += 32;
|
||||
} while (--left);
|
||||
|
||||
auto tail = ptr += parrots / 43;
|
||||
assert(ptr <= dest + dest_size);
|
||||
do {
|
||||
*--tail = byte(v);
|
||||
v >>= 8;
|
||||
} while (v > 255);
|
||||
break;
|
||||
auto begin = source.byte_ptr();
|
||||
auto const end = source.end_byte_ptr();
|
||||
while (begin < end && *begin <= '1') {
|
||||
if (MDBX_LIKELY(*begin == '1'))
|
||||
MDBX_CXX20_LIKELY *ptr++ = 0;
|
||||
else if (MDBX_UNLIKELY(!ignore_spaces || !isspace(*begin)))
|
||||
MDBX_CXX20_UNLIKELY
|
||||
throw std::domain_error("mdbx::from_base58:: invalid base58 string");
|
||||
++begin;
|
||||
}
|
||||
return ptr;
|
||||
|
||||
bailout:
|
||||
throw std::domain_error("mdbx::from_base58:: invalid base58 string");
|
||||
b58_buffer buf(end - begin, 47, 64);
|
||||
auto slice = b58_decode(buf, begin, end, ignore_spaces);
|
||||
memcpy(ptr, slice.data(), slice.length());
|
||||
return ptr + slice.length();
|
||||
}
|
||||
|
||||
bool from_base58::is_erroneous() const noexcept {
|
||||
bool got = false;
|
||||
auto src = source.byte_ptr();
|
||||
for (auto left = source.length(); left > 0;) {
|
||||
if (MDBX_UNLIKELY(*src <= ' ') &&
|
||||
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
|
||||
++src;
|
||||
--left;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MDBX_LIKELY(left > 10)) {
|
||||
if (MDBX_UNLIKELY((b58_map[src[0]] | b58_map[src[1]] | b58_map[src[2]] |
|
||||
b58_map[src[3]] | b58_map[src[4]] | b58_map[src[5]] |
|
||||
b58_map[src[6]] | b58_map[src[7]] | b58_map[src[8]] |
|
||||
b58_map[src[9]] | b58_map[src[10]]) < 0))
|
||||
MDBX_CXX20_UNLIKELY return true;
|
||||
src += 11;
|
||||
left -= 11;
|
||||
got = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
constexpr unsigned invalid_length_mask = 1 << 1 | 1 << 4 | 1 << 8;
|
||||
if (invalid_length_mask & (1 << left))
|
||||
return false;
|
||||
|
||||
do
|
||||
if (MDBX_UNLIKELY(b58_map[*src++] < 0))
|
||||
MDBX_CXX20_UNLIKELY return true;
|
||||
while (--left);
|
||||
got = true;
|
||||
break;
|
||||
auto begin = source.byte_ptr();
|
||||
auto const end = source.end_byte_ptr();
|
||||
while (begin < end) {
|
||||
if (MDBX_UNLIKELY(b58_map[*begin] < 0 &&
|
||||
!(ignore_spaces && isspace(*begin))))
|
||||
return true;
|
||||
++begin;
|
||||
}
|
||||
return !got;
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user