ULID Support (#5129)

This commit is contained in:
Günter Obiltschnig
2025-12-19 19:58:33 +01:00
committed by GitHub
parent 5bbd542b6b
commit b24d9ff0b8
22 changed files with 1623 additions and 94 deletions

3
.gitignore vendored
View File

@@ -179,6 +179,9 @@ node_modules
# Build-time symlinks to bundled libraries #
############################################
Foundation/src/adler32.c
Foundation/src/config.h
Foundation/src/pcre2posix.c
Foundation/src/pcre2posix.h
Foundation/src/compress.c
Foundation/src/crc32.c
Foundation/src/crc32.h

View File

@@ -33,7 +33,7 @@ objects = ArchiveStrategy Ascii ASCIIEncoding AsyncChannel AsyncNotificationCent
ThreadPool ThreadTarget ActiveDispatcher Timer Timespan Timestamp Timezone Token URI \
FileStreamFactory URIStreamFactory URIStreamOpener UTF32Encoding UTF16Encoding UTF8Encoding UTF8String \
Unicode UnicodeConverter Windows1250Encoding Windows1251Encoding Windows1252Encoding \
UUID UUIDGenerator Void Var VarHolder VarIterator VarVisitor Format Pipe PipeImpl PipeStream SharedMemory \
UUID UUIDGenerator ULID ULIDGenerator Void Var VarHolder VarIterator VarVisitor Format Pipe PipeImpl PipeStream SharedMemory \
MemoryStream FileStream AtomicCounter DataURIStream DataURIStreamFactory FileStreamRWLock \
BufferedBidirectionalStreamBuf BufferedStreamBuf UnbufferedStreamBuf

View File

@@ -7,7 +7,7 @@
//
// Definition of class Base32Decoder.
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// Copyright (c) 2004-2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
@@ -37,20 +37,24 @@ class Foundation_API Base32DecoderBuf: public UnbufferedStreamBuf
/// its streambuf.
{
public:
Base32DecoderBuf(std::istream& istr);
Base32DecoderBuf(std::istream& istr, int options = 0);
~Base32DecoderBuf() override;
private:
int readFromDevice() override;
int readOne();
static const unsigned char* encoding(int options);
unsigned char _group[8];
int _groupLength;
int _groupIndex;
std::streambuf& _buf;
const unsigned char* _encoding;
static unsigned char IN_ENCODING[256];
static bool IN_ENCODING_INIT;
static const unsigned char REVERSE_DEFAULT_ENCODING[256];
static const unsigned char REVERSE_HEX_ENCODING[256];
static const unsigned char REVERSE_CROCKFORD_ENCODING[256];
private:
Base32DecoderBuf(const Base32DecoderBuf&);
@@ -65,7 +69,7 @@ class Foundation_API Base32DecoderIOS: public virtual std::ios
/// order of the stream buffer and base classes.
{
public:
Base32DecoderIOS(std::istream& istr);
Base32DecoderIOS(std::istream& istr, int options = 0);
~Base32DecoderIOS() override;
Base32DecoderBuf* rdbuf();
@@ -83,6 +87,8 @@ class Foundation_API Base32Decoder: public Base32DecoderIOS, public std::istream
/// read from the istream connected to it.
///
/// The class implements RFC 4648 - https://tools.ietf.org/html/rfc4648
/// and additionally supports decoding of Crockford Base 32 encoded
/// data.
///
/// Note: For performance reasons, the characters
/// are read directly from the given istream's
@@ -91,7 +97,10 @@ class Foundation_API Base32Decoder: public Base32DecoderIOS, public std::istream
/// its streambuf.
{
public:
Base32Decoder(std::istream& istr);
Base32Decoder(std::istream& istr, int options = 0);
/// Creates the Base32Decoder with the given options.
/// See Base32EncodingOptions for valid options.
~Base32Decoder() override;
private:

View File

@@ -7,7 +7,7 @@
//
// Definition of class Base32Encoder.
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// Copyright (c) 2004-2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
@@ -26,6 +26,33 @@
namespace Poco {
enum Base32EncodingOptions
{
BASE32_NO_PADDING = 0x00,
/// Don't append padding characters ('=') at end.
///
/// NOTE: This is provided for consistency with Base64EncodingOptions.
BASE32_USE_PADDING = 0x01,
/// Append padding characters ('=') at end.
///
/// NOTE: This is provided for backwards compatibility with constructor
/// taking a bool padding argument.
BASE32_USE_HEX_ALPHABET = 0x02,
/// Use the RFC 4648 Base32 Extended Hex alphabet.
BASE32_USE_CROCKFORD_ALPHABET = 0x04
/// Use Crockford's Base 32 alphabet for encoding
/// (https://www.crockford.com/base32.html)
/// instead of RFC 4648 Base32 alphabet.
///
/// Note: decoding is not case sensitive.
/// Furthermore characters 0/O/o and 1/I/i/L/l
/// are decoded as 0 and 1, respectively.
};
class Foundation_API Base32EncoderBuf: public UnbufferedStreamBuf
/// This streambuf base32-encodes all data written
/// to it and forwards it to a connected
@@ -37,12 +64,17 @@ class Foundation_API Base32EncoderBuf: public UnbufferedStreamBuf
/// not updated to match the buffer's state.
{
public:
Base32EncoderBuf(std::ostream& ostr, bool padding = true);
Base32EncoderBuf(std::ostream& ostr, int options = BASE32_USE_PADDING);
~Base32EncoderBuf() override;
int close();
/// Closes the stream buffer.
protected:
static const unsigned char* encoding(int options);
/// Returns the alphabet to be used for encoding/decoding
/// according to the specified options.
private:
int writeToDevice(char c) override;
@@ -50,8 +82,11 @@ private:
int _groupLength;
std::streambuf& _buf;
bool _doPadding;
const unsigned char* _encoding;
static const unsigned char OUT_ENCODING[32];
static const unsigned char DEFAULT_ENCODING[32];
static const unsigned char HEX_ENCODING[32];
static const unsigned char CROCKFORD_ENCODING[32];
friend class Base32DecoderBuf;
@@ -67,7 +102,7 @@ class Foundation_API Base32EncoderIOS: public virtual std::ios
/// order of the stream buffer and base classes.
{
public:
Base32EncoderIOS(std::ostream& ostr, bool padding = true);
Base32EncoderIOS(std::ostream& ostr, int options = BASE32_USE_PADDING);
~Base32EncoderIOS() override;
int close();
Base32EncoderBuf* rdbuf();
@@ -90,6 +125,7 @@ class Foundation_API Base32Encoder: public Base32EncoderIOS, public std::ostream
/// completion of the encoding operation.
///
/// The class implements RFC 4648 - https://tools.ietf.org/html/rfc4648
/// and additionally supports Crockford's Base 32.
///
/// Note: The characters are directly written
/// to the ostream's streambuf, thus bypassing
@@ -97,7 +133,10 @@ class Foundation_API Base32Encoder: public Base32EncoderIOS, public std::ostream
/// not updated to match the buffer's state.
{
public:
Base32Encoder(std::ostream& ostr, bool padding = true);
Base32Encoder(std::ostream& ostr, int options = BASE32_USE_PADDING);
/// Creates the Base32Encoder with the given options.
/// See Base32EncodingOptions for supported options.
~Base32Encoder() override;
private:

View File

@@ -0,0 +1,200 @@
//
// ULID.h
//
// Library: Foundation
// Package: ULID
// Module: ULID
//
// Definition of the ULID class.
//
// Copyright (c) 2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Foundation_ULID_INCLUDED
#define Foundation_ULID_INCLUDED
#include "Poco/Foundation.h"
namespace Poco {
class Foundation_API ULID
/// This class implements a [Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec) (ULID).
///
/// To generate a new random ULID, see ULIDGenerator::create().
{
public:
ULID() = default;
/// Creates a nil (all zero) ULID.
ULID(const ULID& ulid) = default;
/// Copy constructor.
explicit ULID(const std::string& ulid);
/// Parses the ULID from a string.
explicit ULID(const char* ulid);
/// Parses the ULID from a string.
ULID(UInt64 time, UInt16 randomHigh, UInt64 randomLow);
/// Creates a ULID from its components.
~ULID() = default;
/// Destroys the ULID.
ULID& operator = (const ULID& ulid);
/// Assignment operator.
void swap(ULID& ulid) noexcept;
/// Swaps the ULID with another one.
void parse(const std::string& ulid);
/// Parses the ULID from its string representation.
bool tryParse(const std::string& ulid);
/// Tries to interpret the given string as an ULID.
/// If the ULID is syntactically valid, assigns the
/// members and returns true. Otherwise leaves the
/// object unchanged and returns false.
std::string toString() const;
/// Returns a string representation of the ULID,
/// using Crockford's Base 32 encoding.
void copyFrom(const char* buffer);
/// Copies the ULID (16 bytes) from a buffer or byte array.
/// The ULID fields are expected to be
/// stored in network byte order.
/// The buffer need not be aligned.
void copyTo(char* buffer) const;
/// Copies the ULID to the buffer. The fields
/// are in network byte order.
/// The buffer need not be aligned.
/// There must have room for at least 16 bytes.
Poco::UInt64 time() const;
/// Returns the 48-bit time part of the ULID
/// (milliseconds since Unix epoch).
Poco::UInt16 randomHigh() const;
/// Returns the most significant 16 bits of the 80-bit
/// random part of the ULID.
Poco::UInt64 randomLow() const;
/// Returns the least significant 64 bits of the 80-bit
/// random part of the ULID.
bool operator == (const ULID& ulid) const;
bool operator != (const ULID& ulid) const;
bool operator < (const ULID& ulid) const;
bool operator <= (const ULID& ulid) const;
bool operator > (const ULID& ulid) const;
bool operator >= (const ULID& ulid) const;
bool isNull() const;
/// Returns true iff the ULID is nil (in other words,
/// consists of all zeros).
static const ULID& null();
/// Returns a null/nil ULID.
protected:
int compare(const ULID& ulid) const;
template <typename T>
static T decode(char c)
{
return static_cast<T>(REVERSE_ENCODING[static_cast<unsigned char>(c)]);
}
private:
UInt32 _timeHi = 0;
UInt16 _timeLo = 0;
UInt16 _randomHi = 0;
UInt64 _randomLo = 0;
static const char ENCODING[32];
static const UInt8 REVERSE_ENCODING[256];
};
//
// inlines
//
inline Poco::UInt64 ULID::time() const
{
return (static_cast<Poco::UInt64>(_timeHi) << 16) + _timeLo;
}
inline Poco::UInt16 ULID::randomHigh() const
{
return _randomHi;
}
inline Poco::UInt64 ULID::randomLow() const
{
return _randomLo;
}
inline bool ULID::operator == (const ULID& ulid) const
{
return compare(ulid) == 0;
}
inline bool ULID::operator != (const ULID& ulid) const
{
return compare(ulid) != 0;
}
inline bool ULID::operator < (const ULID& ulid) const
{
return compare(ulid) < 0;
}
inline bool ULID::operator <= (const ULID& ulid) const
{
return compare(ulid) <= 0;
}
inline bool ULID::operator > (const ULID& ulid) const
{
return compare(ulid) > 0;
}
inline bool ULID::operator >= (const ULID& ulid) const
{
return compare(ulid) >= 0;
}
inline bool ULID::isNull() const
{
return compare(null()) == 0;
}
inline void swap(ULID& u1, ULID& u2) noexcept
{
u1.swap(u2);
}
} // namespace Poco
#endif // Foundation_ULID_INCLUDED

View File

@@ -0,0 +1,71 @@
//
// ULIDGenerator.h
//
// Library: Foundation
// Package: ULID
// Module: ULID
//
// Definition of the ULIDGenerator class.
//
// Copyright (c) 2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Foundation_ULIDGenerator_INCLUDED
#define Foundation_ULIDGenerator_INCLUDED
#include "Poco/Foundation.h"
#include "Poco/ULID.h"
#include "Poco/Mutex.h"
namespace Poco {
class Foundation_API ULIDGenerator
/// This class implements a generator for
/// [Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec) (ULID)s.
{
public:
ULIDGenerator();
/// Creates the ULIDGenerator.
~ULIDGenerator();
/// Destroys the ULIDGenerator.
ULID create();
/// Creates a new ULID based on the current time.
///
/// NOTE: In the very rare case that the random part of the ULID overflows due to too many
/// subsequent calls to create() in the same millisecond, create() will wait for the
/// current millisecond to elapse before generating a new ULID.
ULID create(UInt64 time);
/// Creates a new ULID based on the given time (milliseconds
/// since UNIX epoch).
///
/// NOTE: In the very rare case of an overflow of the random part due to too many subsequent
/// calls to create() with the same time, throws a RuntimeException.
static ULIDGenerator& defaultGenerator();
/// Returns a reference to the default ULIDGenerator.
private:
FastMutex _mutex;
Poco::UInt64 _lastTime = 0;
Poco::UInt16 _lastRandomHi = 0;
Poco::UInt64 _lastRandomLo = 0;
ULIDGenerator(const ULIDGenerator&);
ULIDGenerator& operator = (const ULIDGenerator&);
};
} // namespace Poco
#endif // Foundation_ULIDGenerator_INCLUDED

View File

@@ -42,6 +42,8 @@ class Foundation_API UUID
/// and also http://tools.ietf.org/html/draft-mealling-uuid-urn-05
///
/// Version 6 and 7 UUIDs are based on RFC 9562.
///
/// To generate a UUID, see UUIDGenerator.
{
public:
enum Version

View File

@@ -5,7 +5,7 @@
// Package: Streams
// Module: Base32
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// Copyright (c) 2004-2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
@@ -15,42 +15,129 @@
#include "Poco/Base32Decoder.h"
#include "Poco/Base32Encoder.h"
#include "Poco/Exception.h"
#include "Poco/Mutex.h"
#include <cstring>
namespace Poco {
unsigned char Base32DecoderBuf::IN_ENCODING[256];
bool Base32DecoderBuf::IN_ENCODING_INIT = false;
namespace
const unsigned char Base32DecoderBuf::REVERSE_DEFAULT_ENCODING[256] =
{
static FastMutex mutex;
}
/* 00 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 08 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 10 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 18 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 20 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 28 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 30 */ 0xFF, 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
/* 38 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
/* 40 */ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
/* 48 */ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
/* 50 */ 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
/* 58 */ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 60 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 68 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 70 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 78 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 80 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 88 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 90 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 98 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* B0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* B8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* C0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* C8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* D0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* D8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* E0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* E8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* F0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* F8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
Base32DecoderBuf::Base32DecoderBuf(std::istream& istr):
const unsigned char Base32DecoderBuf::REVERSE_HEX_ENCODING[256] =
{
/* 00 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 08 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 10 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 18 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 20 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 28 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 30 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
/* 38 */ 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
/* 40 */ 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
/* 48 */ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
/* 50 */ 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF,
/* 58 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 60 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 68 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 70 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 78 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 80 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 88 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 90 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 98 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* B0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* B8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* C0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* C8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* D0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* D8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* E0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* E8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* F0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* F8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
const unsigned char Base32DecoderBuf::REVERSE_CROCKFORD_ENCODING[256] =
{
/* 00 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 08 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 10 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 18 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 20 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 28 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 30 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
/* 38 */ 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
/* 40 */ 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
/* 48 */ 0x11, 0x01, 0x12, 0x13, 0x01, 0x14, 0x15, 0x00,
/* 50 */ 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C,
/* 58 */ 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 60 */ 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
/* 68 */ 0x11, 0x01, 0x12, 0x13, 0x01, 0x14, 0x15, 0x00,
/* 70 */ 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C,
/* 78 */ 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 80 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 88 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 90 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 98 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* B0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* B8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* C0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* C8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* D0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* D8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* E0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* E8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* F0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* F8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
Base32DecoderBuf::Base32DecoderBuf(std::istream& istr, int options):
_groupLength(0),
_groupIndex(0),
_buf(*istr.rdbuf())
_buf(*istr.rdbuf()),
_encoding(encoding(options))
{
FastMutex::ScopedLock lock(mutex);
if (!IN_ENCODING_INIT)
{
for (unsigned i = 0; i < sizeof(IN_ENCODING); i++)
{
IN_ENCODING[i] = 0xFF;
}
for (unsigned i = 0; i < sizeof(Base32EncoderBuf::OUT_ENCODING); i++)
{
IN_ENCODING[Base32EncoderBuf::OUT_ENCODING[i]] = static_cast<UInt8>(i);
}
IN_ENCODING[static_cast<unsigned char>('=')] = '\0';
IN_ENCODING_INIT = true;
}
}
@@ -74,38 +161,40 @@ int Base32DecoderBuf::readFromDevice()
// per RFC-4648, Section 6, permissible block lengths are:
// 2, 4, 5, 7, and 8 bytes. Any other length is malformed.
//
do {
do
{
if ((c = readOne()) == -1) return -1;
buffer[0] = (unsigned char) c;
if (IN_ENCODING[buffer[0]] == 0xFF) throw DataFormatException();
if (_encoding[buffer[0]] == 0xFF) throw DataFormatException();
if ((c = readOne()) == -1) throw DataFormatException();
buffer[1] = (unsigned char) c;
if (IN_ENCODING[buffer[1]] == 0xFF) throw DataFormatException();
if (_encoding[buffer[1]] == 0xFF) throw DataFormatException();
if ((c = readOne()) == -1) break;
buffer[2] = (unsigned char) c;
if (IN_ENCODING[buffer[2]] == 0xFF) throw DataFormatException();
if (_encoding[buffer[2]] == 0xFF) throw DataFormatException();
if ((c = readOne()) == -1) throw DataFormatException();
buffer[3] = (unsigned char) c;
if (IN_ENCODING[buffer[3]] == 0xFF) throw DataFormatException();
if (_encoding[buffer[3]] == 0xFF) throw DataFormatException();
if ((c = readOne()) == -1) break;
buffer[4] = (unsigned char) c;
if (IN_ENCODING[buffer[4]] == 0xFF) throw DataFormatException();
if (_encoding[buffer[4]] == 0xFF) throw DataFormatException();
if ((c = readOne()) == -1) break;
buffer[5] = (unsigned char) c;
if (IN_ENCODING[buffer[5]] == 0xFF) throw DataFormatException();
if (_encoding[buffer[5]] == 0xFF) throw DataFormatException();
if ((c = readOne()) == -1) throw DataFormatException();
buffer[6] = (unsigned char) c;
if (IN_ENCODING[buffer[6]] == 0xFF) throw DataFormatException();
if (_encoding[buffer[6]] == 0xFF) throw DataFormatException();
if ((c = readOne()) == -1) break;
buffer[7] = (unsigned char) c;
if (IN_ENCODING[buffer[7]] == 0xFF) throw DataFormatException();
} while (false);
if (_encoding[buffer[7]] == 0xFF) throw DataFormatException();
}
while (false);
_group[0] = (IN_ENCODING[buffer[0]] << 3) | (IN_ENCODING[buffer[1]] >> 2);
_group[1] = ((IN_ENCODING[buffer[1]] & 0x03) << 6) | (IN_ENCODING[buffer[2]] << 1) | (IN_ENCODING[buffer[3]] >> 4);
_group[2] = ((IN_ENCODING[buffer[3]] & 0x0F) << 4) | (IN_ENCODING[buffer[4]] >> 1);
_group[3] = ((IN_ENCODING[buffer[4]] & 0x01) << 7) | (IN_ENCODING[buffer[5]] << 2) | (IN_ENCODING[buffer[6]] >> 3);
_group[4] = ((IN_ENCODING[buffer[6]] & 0x07) << 5) | IN_ENCODING[buffer[7]];
_group[0] = (_encoding[buffer[0]] << 3) | (_encoding[buffer[1]] >> 2);
_group[1] = ((_encoding[buffer[1]] & 0x03) << 6) | (_encoding[buffer[2]] << 1) | (_encoding[buffer[3]] >> 4);
_group[2] = ((_encoding[buffer[3]] & 0x0F) << 4) | (_encoding[buffer[4]] >> 1);
_group[3] = ((_encoding[buffer[4]] & 0x01) << 7) | (_encoding[buffer[5]] << 2) | (_encoding[buffer[6]] >> 3);
_group[4] = ((_encoding[buffer[6]] & 0x07) << 5) | _encoding[buffer[7]];
if (buffer[2] == '=')
_groupLength = 1;
@@ -130,7 +219,18 @@ int Base32DecoderBuf::readOne()
}
Base32DecoderIOS::Base32DecoderIOS(std::istream& istr): _buf(istr)
const unsigned char* Base32DecoderBuf::encoding(int options)
{
if ((options & BASE32_USE_HEX_ALPHABET) != 0)
return REVERSE_HEX_ENCODING;
else if ((options & BASE32_USE_CROCKFORD_ALPHABET) != 0)
return REVERSE_CROCKFORD_ENCODING;
else
return REVERSE_DEFAULT_ENCODING;
}
Base32DecoderIOS::Base32DecoderIOS(std::istream& istr, int options): _buf(istr, options)
{
poco_ios_init(&_buf);
}
@@ -147,7 +247,7 @@ Base32DecoderBuf* Base32DecoderIOS::rdbuf()
}
Base32Decoder::Base32Decoder(std::istream& istr): Base32DecoderIOS(istr), std::istream(&_buf)
Base32Decoder::Base32Decoder(std::istream& istr, int options): Base32DecoderIOS(istr, options), std::istream(&_buf)
{
}

View File

@@ -5,7 +5,7 @@
// Package: Streams
// Module: Base32
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// Copyright (c) 2004-2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
@@ -18,19 +18,38 @@
namespace Poco {
const unsigned char Base32EncoderBuf::OUT_ENCODING[32] =
const unsigned char Base32EncoderBuf::DEFAULT_ENCODING[32] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
'Y', 'Z', '2', '3', '4', '5', '6', '7'
};
Base32EncoderBuf::Base32EncoderBuf(std::ostream& ostr, bool padding):
const unsigned char Base32EncoderBuf::HEX_ENCODING[32] =
{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'
};
const unsigned char Base32EncoderBuf::CROCKFORD_ENCODING[32] =
{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'
};
Base32EncoderBuf::Base32EncoderBuf(std::ostream& ostr, int options):
_groupLength(0),
_buf(*ostr.rdbuf()),
_doPadding(padding)
_doPadding((options & BASE32_USE_PADDING) != 0),
_encoding(encoding(options))
{
}
@@ -47,7 +66,6 @@ Base32EncoderBuf::~Base32EncoderBuf()
}
int Base32EncoderBuf::writeToDevice(char c)
{
static const int eof = std::char_traits<char>::eof();
@@ -57,21 +75,21 @@ int Base32EncoderBuf::writeToDevice(char c)
{
unsigned char idx;
idx = _group[0] >> 3;
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[0] & 0x07) << 2) | (_group[1] >> 6);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[1] & 0x3E) >> 1);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[1] & 0x01) << 4) | (_group[2] >> 4);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[2] & 0x0F) << 1) | (_group[3] >> 7);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[3] & 0x7C) >> 2);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[3] & 0x03) << 3) | (_group[4] >> 5);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = (_group[4] & 0x1F);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
_groupLength = 0;
}
return charToInt(c);
@@ -88,10 +106,11 @@ int Base32EncoderBuf::close()
_group[1] = 0;
unsigned char idx;
idx = _group[0] >> 3;
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[0] & 0x07) << 2);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_doPadding) {
if (_buf.sputc(_encoding[idx]) == eof) return eof;
if (_doPadding)
{
if (_buf.sputc('=') == eof) return eof;
if (_buf.sputc('=') == eof) return eof;
if (_buf.sputc('=') == eof) return eof;
@@ -105,14 +124,15 @@ int Base32EncoderBuf::close()
_group[2] = 0;
unsigned char idx;
idx = _group[0] >> 3;
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[0] & 0x07) << 2) | (_group[1] >> 6);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[1] & 0x3E) >> 1);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[1] & 0x01) << 4);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_doPadding) {
if (_buf.sputc(_encoding[idx]) == eof) return eof;
if (_doPadding)
{
if (_buf.sputc('=') == eof) return eof;
if (_buf.sputc('=') == eof) return eof;
if (_buf.sputc('=') == eof) return eof;
@@ -124,16 +144,17 @@ int Base32EncoderBuf::close()
_group[3] = 0;
unsigned char idx;
idx = _group[0] >> 3;
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[0] & 0x07) << 2) | (_group[1] >> 6);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[1] & 0x3E) >> 1);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[1] & 0x01) << 4) | (_group[2] >> 4);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[2] & 0x0F) << 1);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_doPadding) {
if (_buf.sputc(_encoding[idx]) == eof) return eof;
if (_doPadding)
{
if (_buf.sputc('=') == eof) return eof;
if (_buf.sputc('=') == eof) return eof;
if (_buf.sputc('=') == eof) return eof;
@@ -144,19 +165,19 @@ int Base32EncoderBuf::close()
_group[4] = 0;
unsigned char idx;
idx = _group[0] >> 3;
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[0] & 0x07) << 2) | (_group[1] >> 6);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[1] & 0x3E) >> 1);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[1] & 0x01) << 4) | (_group[2] >> 4);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[2] & 0x0F) << 1) | (_group[3] >> 7);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[3] & 0x7C) >> 2);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
idx = ((_group[3] & 0x03) << 3);
if (_buf.sputc(OUT_ENCODING[idx]) == eof) return eof;
if (_buf.sputc(_encoding[idx]) == eof) return eof;
if (_doPadding && _buf.sputc('=') == eof) return eof;
}
_groupLength = 0;
@@ -164,8 +185,19 @@ int Base32EncoderBuf::close()
}
Base32EncoderIOS::Base32EncoderIOS(std::ostream& ostr, bool padding):
_buf(ostr, padding)
const unsigned char* Base32EncoderBuf::encoding(int options)
{
if ((options & BASE32_USE_HEX_ALPHABET) != 0)
return HEX_ENCODING;
else if ((options & BASE32_USE_CROCKFORD_ALPHABET) != 0)
return CROCKFORD_ENCODING;
else
return DEFAULT_ENCODING;
}
Base32EncoderIOS::Base32EncoderIOS(std::ostream& ostr, int options):
_buf(ostr, options)
{
poco_ios_init(&_buf);
}
@@ -188,8 +220,8 @@ Base32EncoderBuf* Base32EncoderIOS::rdbuf()
}
Base32Encoder::Base32Encoder(std::ostream& ostr, bool padding):
Base32EncoderIOS(ostr, padding), std::ostream(&_buf)
Base32Encoder::Base32Encoder(std::ostream& ostr, int options):
Base32EncoderIOS(ostr, options), std::ostream(&_buf)
{
}

275
Foundation/src/ULID.cpp Normal file
View File

@@ -0,0 +1,275 @@
//
// ULID.cpp
//
// Library: Foundation
// Package: ULID
// Module: ULID
//
// Copyright (c) 2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/ULID.h"
#include "Poco/Exception.h"
#include "Poco/ByteOrder.h"
#include <algorithm>
#include <cstring>
namespace Poco {
namespace
{
template <typename T>
int cmp(T a, T b)
{
if (a == b)
return 0;
else if (a < b)
return -1;
else
return 1;
}
}
const char ULID::ENCODING[32] =
{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'
};
const UInt8 ULID::REVERSE_ENCODING[256] =
{
/* 00 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 08 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 10 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 18 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 20 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 28 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 30 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
/* 38 */ 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
/* 40 */ 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
/* 48 */ 0x11, 0x01, 0x12, 0x13, 0x01, 0x14, 0x15, 0x00,
/* 50 */ 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C,
/* 58 */ 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 60 */ 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
/* 68 */ 0x11, 0x01, 0x12, 0x13, 0x01, 0x14, 0x15, 0x00,
/* 70 */ 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C,
/* 78 */ 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 80 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 88 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 90 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 98 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* B0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* B8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* C0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* C8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* D0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* D8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* E0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* E8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* F0 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* F8 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
ULID::ULID(const std::string& ulid)
{
parse(ulid);
}
ULID::ULID(const char* ulid)
{
poco_check_ptr(ulid);
parse(std::string(ulid));
}
ULID::ULID(UInt64 time, UInt16 randomHi, UInt64 randomLo):
_timeHi(static_cast<UInt32>(time >> 16)),
_timeLo(static_cast<UInt16>(time & 0xFFFF)),
_randomHi(randomHi),
_randomLo(randomLo)
{
}
ULID& ULID::operator = (const ULID& ulid)
{
if (&ulid != this)
{
_timeHi = ulid._timeHi;
_timeLo = ulid._timeLo;
_randomHi = ulid._randomHi;
_randomLo = ulid._randomLo;
}
return *this;
}
void ULID::swap(ULID& ulid) noexcept
{
std::swap(_timeHi, ulid._timeHi);
std::swap(_timeLo, ulid._timeLo);
std::swap(_randomHi, ulid._randomHi);
std::swap(_randomLo, ulid._randomLo);
}
void ULID::parse(const std::string& ulid)
{
if (!tryParse(ulid))
throw SyntaxException(ulid);
}
bool ULID::tryParse(const std::string& ulid)
{
if (ulid.size() != 26) return false;
if (ulid[0] > '7' && !(ulid[0] == 'l' || ulid[0] == 'L' || ulid[0] == 'i' || ulid[0] == 'I' || ulid[0] == 'o' || ulid[0] == 'O')) return false;
for (auto c: ulid)
{
if (REVERSE_ENCODING[static_cast<unsigned char>(c)] == 0xFF) return false;
}
_timeHi = (decode<UInt32>(ulid[0]) << 29)
| (decode<UInt32>(ulid[1]) << 24)
| (decode<UInt32>(ulid[2]) << 19)
| (decode<UInt32>(ulid[3]) << 14)
| (decode<UInt32>(ulid[4]) << 9)
| (decode<UInt32>(ulid[5]) << 4)
| (decode<UInt32>(ulid[6]) >> 1);
_timeLo = ((decode<UInt16>(ulid[6]) & 1) << 15)
| (decode<UInt16>(ulid[7]) << 10)
| (decode<UInt16>(ulid[8]) << 5)
| decode<UInt16>(ulid[9]);
_randomHi = (decode<UInt16>(ulid[10]) << 11)
| (decode<UInt16>(ulid[11]) << 6)
| (decode<UInt16>(ulid[12]) << 1)
| (decode<UInt16>(ulid[13]) >> 4);
_randomLo = ((decode<UInt64>(ulid[13]) & 0xF) << 60)
| (decode<UInt64>(ulid[14]) << 55)
| (decode<UInt64>(ulid[15]) << 50)
| (decode<UInt64>(ulid[16]) << 45)
| (decode<UInt64>(ulid[17]) << 40)
| (decode<UInt64>(ulid[18]) << 35)
| (decode<UInt64>(ulid[19]) << 30)
| (decode<UInt64>(ulid[20]) << 25)
| (decode<UInt64>(ulid[21]) << 20)
| (decode<UInt64>(ulid[22]) << 15)
| (decode<UInt64>(ulid[23]) << 10)
| (decode<UInt64>(ulid[24]) << 5)
| decode<UInt64>(ulid[25]);
return true;
}
std::string ULID::toString() const
{
std::string result(26, 0);
result[0] = ENCODING[_timeHi >> 29];
result[1] = ENCODING[_timeHi >> 24 & 0x1F];
result[2] = ENCODING[_timeHi >> 19 & 0x1F];
result[3] = ENCODING[_timeHi >> 14 & 0x1F];
result[4] = ENCODING[_timeHi >> 9 & 0x1F];
result[5] = ENCODING[_timeHi >> 4 & 0x1F];
result[6] = ENCODING[((_timeHi & 0x0F) << 1) + (_timeLo >> 15)];
result[7] = ENCODING[_timeLo >> 10 & 0x1F];
result[8] = ENCODING[_timeLo >> 5 & 0x1F];
result[9] = ENCODING[_timeLo & 0x1F];
result[10] = ENCODING[_randomHi >> 11 & 0x1F];
result[11] = ENCODING[_randomHi >> 6 & 0x1F];
result[12] = ENCODING[_randomHi >> 1 & 0x1F];
result[13] = ENCODING[((_randomHi & 0x01) << 4) + (_randomLo >> 60)];
result[14] = ENCODING[_randomLo >> 55 & 0x1F];
result[15] = ENCODING[_randomLo >> 50 & 0x1F];
result[16] = ENCODING[_randomLo >> 45 & 0x1F];
result[17] = ENCODING[_randomLo >> 40 & 0x1F];
result[18] = ENCODING[_randomLo >> 35 & 0x1F];
result[19] = ENCODING[_randomLo >> 30 & 0x1F];
result[20] = ENCODING[_randomLo >> 25 & 0x1F];
result[21] = ENCODING[_randomLo >> 20 & 0x1F];
result[22] = ENCODING[_randomLo >> 15 & 0x1F];
result[23] = ENCODING[_randomLo >> 10 & 0x1F];
result[24] = ENCODING[_randomLo >> 5 & 0x1F];
result[25] = ENCODING[_randomLo & 0x1F];
return result;
}
void ULID::copyFrom(const char* buffer)
{
Poco::UInt32 timeHi;
std::memcpy(&timeHi, buffer, 4);
_timeHi = Poco::ByteOrder::fromNetwork(timeHi);
Poco::UInt16 timeLo;
std::memcpy(&timeLo, buffer + 4, 2);
_timeLo = Poco::ByteOrder::fromNetwork(timeLo);
Poco::UInt16 randomHi;
std::memcpy(&randomHi, buffer + 6, 2);
_randomHi = Poco::ByteOrder::fromNetwork(randomHi);
Poco::UInt64 randomLo;
std::memcpy(&randomLo, buffer + 8, 8);
_randomLo = Poco::ByteOrder::fromNetwork(randomLo);
}
void ULID::copyTo(char* buffer) const
{
Poco::UInt32 timeHi = Poco::ByteOrder::toNetwork(_timeHi);
std::memcpy(buffer, &timeHi, 4);
Poco::UInt16 timeLo = Poco::ByteOrder::toNetwork(_timeLo);
std::memcpy(buffer + 4, &timeLo, 2);
Poco::UInt16 randomHi = Poco::ByteOrder::toNetwork(_randomHi);
std::memcpy(buffer + 6, &randomHi, 2);
Poco::UInt64 randomLo = Poco::ByteOrder::toNetwork(_randomLo);
std::memcpy(buffer + 8, &randomLo, 8);
}
const ULID& ULID::null()
{
static const ULID nil;
return nil;
}
int ULID::compare(const ULID& ulid) const
{
int c = cmp(_timeHi, ulid._timeHi);
if (c == 0)
{
c = cmp(_timeLo, ulid._timeLo);
if (c == 0)
{
c = cmp(_randomHi, ulid._randomHi);
if (c == 0)
{
c = cmp(_randomLo, ulid._randomLo);
}
}
}
return c;
}
} // namespace Poco

View File

@@ -0,0 +1,116 @@
//
// ULIDGenerator.cpp
//
// Library: Foundation
// Package: ULID
// Module: ULID
//
// Copyright (c) 2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/ULIDGenerator.h"
#include "Poco/RandomStream.h"
#include "Poco/BinaryReader.h"
#include "Poco/Exception.h"
namespace Poco {
ULIDGenerator::ULIDGenerator()
{
}
ULIDGenerator::~ULIDGenerator()
{
}
ULID ULIDGenerator::create()
{
Poco::Timestamp now;
Poco::UInt64 time = now.epochMicroseconds()/1000;
Poco::UInt16 lastRandomHi;
Poco::UInt64 lastRandomLo;
{
Poco::FastMutex::ScopedLock lock(_mutex);
while (true)
{
if (time == _lastTime)
{
if (++_lastRandomLo == 0)
{
if (++_lastRandomHi == 0)
{
// random overflow, wait until next millisecond
Poco::UInt64 ntime = time;
while (ntime == time)
{
now.update();
ntime = now.epochMicroseconds()/1000;
}
time = ntime;
continue;
}
}
}
else
{
_lastTime = time;
Poco::RandomInputStream random;
Poco::BinaryReader reader(random, Poco::BinaryReader::NETWORK_BYTE_ORDER);
reader >> _lastRandomHi >> _lastRandomLo;
}
lastRandomHi = _lastRandomHi;
lastRandomLo = _lastRandomLo;
break;
}
}
return ULID(time, lastRandomHi, lastRandomLo);
}
ULID ULIDGenerator::create(UInt64 time)
{
Poco::UInt16 lastRandomHi;
Poco::UInt64 lastRandomLo;
{
Poco::FastMutex::ScopedLock lock(_mutex);
if (time == _lastTime)
{
if (++_lastRandomLo == 0)
{
if (++_lastRandomHi == 0) throw RuntimeException("Too many ULIDs created in a single millisecond");
}
}
else
{
_lastTime = time;
Poco::RandomInputStream random;
Poco::BinaryReader reader(random, Poco::BinaryReader::NETWORK_BYTE_ORDER);
reader >> _lastRandomHi >> _lastRandomLo;
}
lastRandomHi = _lastRandomHi;
lastRandomLo = _lastRandomLo;
}
return ULID(time, lastRandomHi, lastRandomLo);
}
ULIDGenerator& ULIDGenerator::defaultGenerator()
{
static ULIDGenerator gen;
return gen;
}
} // namespace Poco

View File

@@ -30,7 +30,8 @@ objects = ActiveMethodTest ActivityTest ActiveDispatcherTest \
TextConverterTest TextIteratorTest TextBufferIteratorTest TextTestSuite TextEncodingTest \
ThreadLocalTest ThreadPoolTest ActiveThreadPoolTest ThreadTest ThreadingTestSuite TimerTest \
TimespanTest TimestampTest TimezoneTest URIStreamOpenerTest URITest \
URITestSuite UUIDGeneratorTest UUIDTest UUIDTestSuite ZLibTest \
URITestSuite UUIDGeneratorTest UUIDTest UUIDTestSuite \
ULIDTest ULIDGeneratorTest ULIDTestSuite ZLibTest \
TestPlugin DummyDelegate BasicEventTest FIFOEventTest PriorityEventTest EventTestSuite \
LRUCacheTest ExpireCacheTest ExpireLRUCacheTest CacheTestSuite AnyTest FormatTest \
HashingTestSuite HashTableTest SimpleHashTableTest LinearHashTableTest \

View File

@@ -72,6 +72,125 @@ void Base32Test::testEncoder()
}
void Base32Test::testEncoderHex()
{
// RFC 4648 test vectors
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET | Poco::BASE32_USE_PADDING);
encoder.close();
assertTrue (str.str() == "");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET | Poco::BASE32_USE_PADDING);
encoder << "f";
encoder.close();
assertTrue (str.str() == "CO======");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET | Poco::BASE32_USE_PADDING);
encoder << "fo";
encoder.close();
assertTrue (str.str() == "CPNG====");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET | Poco::BASE32_USE_PADDING);
encoder << "foo";
encoder.close();
assertTrue (str.str() == "CPNMU===");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET | Poco::BASE32_USE_PADDING);
encoder << "foob";
encoder.close();
assertTrue (str.str() == "CPNMUOG=");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET | Poco::BASE32_USE_PADDING);
encoder << "fooba";
encoder.close();
assertTrue (str.str() == "CPNMUOJ1");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET | Poco::BASE32_USE_PADDING);
encoder << "foobar";
encoder.close();
assertTrue (str.str() == "CPNMUOJ1E8======");
}
}
void Base32Test::testEncoderCrockford()
{
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder.close();
assertTrue (str.str() == "");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder << "f";
encoder.close();
assertTrue (str.str() == "CR");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder << "fo";
encoder.close();
assertTrue (str.str() == "CSQG");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder << "foo";
encoder.close();
assertTrue (str.str() == "CSQPY");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder << "foob";
encoder.close();
assertTrue (str.str() == "CSQPYRG");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder << "fooba";
encoder.close();
assertTrue (str.str() == "CSQPYRK1");
}
{
std::ostringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder << "foobar";
encoder.close();
assertTrue (str.str() == "CSQPYRK1E8");
}
}
void Base32Test::testDecoder()
{
{
@@ -130,6 +249,108 @@ void Base32Test::testDecoder()
}
void Base32Test::testDecoderHex()
{
{
std::istringstream istr("CO======");
Base32Decoder decoder(istr, Poco::BASE32_USE_HEX_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "f");
}
{
std::istringstream istr("CPNG====");
Base32Decoder decoder(istr, Poco::BASE32_USE_HEX_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "fo");
}
{
std::istringstream istr("CPNMU===");
Base32Decoder decoder(istr, Poco::BASE32_USE_HEX_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "foo");
}
{
std::istringstream istr("CPNMUOG=");
Base32Decoder decoder(istr, Poco::BASE32_USE_HEX_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "foob");
}
{
std::istringstream istr("CPNMUOJ1");
Base32Decoder decoder(istr, Poco::BASE32_USE_HEX_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "fooba");
}
{
std::istringstream istr("CPNMUOJ1E8======");
Base32Decoder decoder(istr, Poco::BASE32_USE_HEX_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "foobar");
}
}
void Base32Test::testDecoderCrockford()
{
{
std::istringstream istr("CR");
Base32Decoder decoder(istr, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "f");
}
{
std::istringstream istr("CSQG");
Base32Decoder decoder(istr, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "fo");
}
{
std::istringstream istr("CSQPY");
Base32Decoder decoder(istr, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "foo");
}
{
std::istringstream istr("CSQPYRG");
Base32Decoder decoder(istr, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "foob");
}
{
std::istringstream istr("CSQPYRK1");
Base32Decoder decoder(istr, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "fooba");
}
{
std::istringstream istr("CSQPYRK1E8");
Base32Decoder decoder(istr, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "foobar");
}
{
// alternative decode symbols (lowercase, 1/l)
std::istringstream istr("csqpyrkle8");
Base32Decoder decoder(istr, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
decoder >> s;
assertTrue (s == "foobar");
}
}
void Base32Test::testEncodeDecode()
{
{
@@ -160,6 +381,66 @@ void Base32Test::testEncodeDecode()
}
void Base32Test::testEncodeDecodeHex()
{
{
std::stringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET);
encoder << "The quick brown fox ";
encoder << "jumped over the lazy dog.";
encoder.close();
Base32Decoder decoder(str, Poco::BASE32_USE_HEX_ALPHABET);
std::string s;
int c = decoder.get();
while (c != -1) { s += char(c); c = decoder.get(); }
assertTrue (s == "The quick brown fox jumped over the lazy dog.");
}
{
std::string src;
for (int i = 0; i < 255; ++i) src += char(i);
std::stringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_HEX_ALPHABET);
encoder.write(src.data(), (std::streamsize) src.size());
encoder.close();
Base32Decoder decoder(str, Poco::BASE32_USE_HEX_ALPHABET);
std::string s;
int c = decoder.get();
while (c != -1) { s += char(c); c = decoder.get(); }
assertTrue (s == src);
}
}
void Base32Test::testEncodeDecodeCrockford()
{
{
std::stringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder << "The quick brown fox ";
encoder << "jumped over the lazy dog.";
encoder.close();
Base32Decoder decoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
int c = decoder.get();
while (c != -1) { s += char(c); c = decoder.get(); }
assertTrue (s == "The quick brown fox jumped over the lazy dog.");
}
{
std::string src;
for (int i = 0; i < 255; ++i) src += char(i);
std::stringstream str;
Base32Encoder encoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
encoder.write(src.data(), (std::streamsize) src.size());
encoder.close();
Base32Decoder decoder(str, Poco::BASE32_USE_CROCKFORD_ALPHABET);
std::string s;
int c = decoder.get();
while (c != -1) { s += char(c); c = decoder.get(); }
assertTrue (s == src);
}
}
void Base32Test::setUp()
{
}
@@ -175,8 +456,14 @@ CppUnit::Test* Base32Test::suite()
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("Base32Test");
CppUnit_addTest(pSuite, Base32Test, testEncoder);
CppUnit_addTest(pSuite, Base32Test, testEncoderHex);
CppUnit_addTest(pSuite, Base32Test, testEncoderCrockford);
CppUnit_addTest(pSuite, Base32Test, testDecoder);
CppUnit_addTest(pSuite, Base32Test, testDecoderHex);
CppUnit_addTest(pSuite, Base32Test, testDecoderCrockford);
CppUnit_addTest(pSuite, Base32Test, testEncodeDecode);
CppUnit_addTest(pSuite, Base32Test, testEncodeDecodeHex);
CppUnit_addTest(pSuite, Base32Test, testEncodeDecodeCrockford);
return pSuite;
}

View File

@@ -25,8 +25,14 @@ public:
~Base32Test();
void testEncoder();
void testEncoderHex();
void testEncoderCrockford();
void testDecoder();
void testDecoderHex();
void testDecoderCrockford();
void testEncodeDecode();
void testEncodeDecodeHex();
void testEncodeDecodeCrockford();
void setUp();
void tearDown();

View File

@@ -19,6 +19,7 @@
#include "LoggingTestSuite.h"
#include "FilesystemTestSuite.h"
#include "UUIDTestSuite.h"
#include "ULIDTestSuite.h"
#include "TextTestSuite.h"
#include "URITestSuite.h"
#if !defined(POCO_VXWORKS)
@@ -44,6 +45,7 @@ CppUnit::Test* FoundationTestSuite::suite()
pSuite->addTest(LoggingTestSuite::suite());
pSuite->addTest(FilesystemTestSuite::suite());
pSuite->addTest(UUIDTestSuite::suite());
pSuite->addTest(ULIDTestSuite::suite());
pSuite->addTest(TextTestSuite::suite());
pSuite->addTest(URITestSuite::suite());
#if !defined(POCO_VXWORKS)

View File

@@ -0,0 +1,86 @@
//
// ULIDGeneratorTest.cpp
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "ULIDGeneratorTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#include "Poco/ULIDGenerator.h"
#include "Poco/ULID.h"
#include <set>
using Poco::ULIDGenerator;
ULIDGeneratorTest::ULIDGeneratorTest(const std::string& name): CppUnit::TestCase(name)
{
}
ULIDGeneratorTest::~ULIDGeneratorTest()
{
}
void ULIDGeneratorTest::testCreate()
{
ULIDGenerator& gen = ULIDGenerator::defaultGenerator();
std::set<Poco::ULID> ulids;
for (int i = 0; i < 1000; ++i)
{
Poco::ULID ulid = gen.create();
assertTrue (ulids.find(ulid) == ulids.end());
ulids.insert(ulid);
}
}
void ULIDGeneratorTest::testCreateTime()
{
const Poco::UInt64 time = static_cast<Poco::UInt64>(1766090811)*1000;
ULIDGenerator gen;
std::set<Poco::ULID> ulids;
for (int i = 0; i < 1000; ++i)
{
try
{
Poco::ULID ulid = gen.create(time);
assertTrue (ulids.find(ulid) == ulids.end());
ulids.insert(ulid);
assertTrue (ulid.time() == time);
}
catch (Poco::RuntimeException&)
{
}
}
}
void ULIDGeneratorTest::setUp()
{
}
void ULIDGeneratorTest::tearDown()
{
}
CppUnit::Test* ULIDGeneratorTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ULIDGeneratorTest");
CppUnit_addTest(pSuite, ULIDGeneratorTest, testCreate);
CppUnit_addTest(pSuite, ULIDGeneratorTest, testCreateTime);
return pSuite;
}

View File

@@ -0,0 +1,39 @@
//
// ULIDGeneratorTest.h
//
// Definition of the ULIDGeneratorTest class.
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ULIDGeneratorTest_INCLUDED
#define ULIDGeneratorTest_INCLUDED
#include "Poco/Foundation.h"
#include "CppUnit/TestCase.h"
class ULIDGeneratorTest: public CppUnit::TestCase
{
public:
ULIDGeneratorTest(const std::string& name);
~ULIDGeneratorTest();
void testCreate();
void testCreateTime();
void setUp();
void tearDown();
static CppUnit::Test* suite();
private:
};
#endif // ULIDGeneratorTest_INCLUDED

View File

@@ -0,0 +1,167 @@
//
// ULIDTest.cpp
//
// Copyright (c) 2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "ULIDTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#include "Poco/ULID.h"
#include "Poco/Exception.h"
ULIDTest::ULIDTest(const std::string& name): CppUnit::TestCase(name)
{
}
ULIDTest::~ULIDTest()
{
}
void ULIDTest::testParse()
{
Poco::ULID ulid("01BX5ZZKBKACTAV9WEVGEMMVRY");
assertTrue (ulid.toString() == "01BX5ZZKBKACTAV9WEVGEMMVRY");
ulid.parse("01bx5zzkbkactav9wevgemmvry");
assertTrue (ulid.toString() == "01BX5ZZKBKACTAV9WEVGEMMVRY");
ulid.parse("olbx5zzkbkactav9wevgemmvry");
assertTrue (ulid.toString() == "01BX5ZZKBKACTAV9WEVGEMMVRY");
ulid.parse("01KCSHHDYBY3J5VRSXHMKNYSE6");
assertTrue (ulid.toString() == "01KCSHHDYBY3J5VRSXHMKNYSE6");
ulid.parse("7ZZZZZZZZZZZZZZZZZZZZZZZZZ");
assertTrue (ulid.toString() == "7ZZZZZZZZZZZZZZZZZZZZZZZZZ");
ulid.parse("00000000000000000000000000");
assertTrue (ulid.toString() == "00000000000000000000000000");
try
{
ulid.parse("80000000000000000000000000");
fail("out of range ULID - must throw");
}
catch (Poco::SyntaxException&)
{
}
try
{
ulid.parse("01BX5ZZKBKACTAV9WEVGEMMVR");
fail("malformed ULID - must throw");
}
catch (Poco::SyntaxException&)
{
}
try
{
ulid.parse("01BX5ZZKBKAUTAV9WEVGEMMVRY");
fail("malformed ULID - must throw");
}
catch (Poco::SyntaxException&)
{
}
try
{
ulid.parse("");
fail("malformed ULID - must throw");
}
catch (Poco::SyntaxException&)
{
}
}
void ULIDTest::testBuffer()
{
Poco::ULID ulid("01BX5ZZKBKACTAV9WEVGEMMVRY");
char buffer[16];
ulid.copyTo(buffer);
Poco::ULID ulid2;
ulid2.copyFrom(buffer);
assertTrue (ulid2.toString() == "01BX5ZZKBKACTAV9WEVGEMMVRY");
}
void ULIDTest::testCompare()
{
Poco::ULID null;
assertTrue (null.isNull());
assertTrue (Poco::ULID::null().isNull());
Poco::ULID ulid1 = null;
Poco::ULID ulid2;
assertTrue (ulid1.isNull());
assertTrue (ulid1 == null);
assertFalse (ulid1 != null);
assertTrue (ulid1 >= null);
assertTrue (ulid1 <= null);
assertFalse (ulid1 > null);
assertFalse (ulid1 < null);
ulid1.parse("01BX5ZZKBKACTAV9WEVGEMMVRY");
ulid2.parse("01BX5ZZKBKACTAV9WEVGEMMVRZ");
assertTrue (ulid1 < ulid2);
assertTrue (ulid1 <= ulid2);
assertTrue (ulid2 > ulid1);
assertTrue (ulid2 >= ulid1);
assertFalse (ulid1 == ulid2);
assertFalse (ulid2 == ulid1);
}
void ULIDTest::testSwap()
{
Poco::ULID ulid1("01BX5ZZKBKACTAV9WEVGEMMVRY");
Poco::ULID ulid2("01BX5ZZKBKACTAV9WEVGEMMVS1");
ulid1.swap(ulid2);
assertTrue (ulid1.toString() == "01BX5ZZKBKACTAV9WEVGEMMVS1");
assertTrue (ulid2.toString() == "01BX5ZZKBKACTAV9WEVGEMMVRY");
}
void ULIDTest::testTryParse()
{
Poco::ULID ulid;
assertTrue (ulid.tryParse("01BX5ZZKBKACTAV9WEVGEMMVRY"));
assertTrue (ulid.toString() == "01BX5ZZKBKACTAV9WEVGEMMVRY");
Poco::ULID notUlid;
assertTrue (!notUlid.tryParse("not a uuid"));
assertTrue (notUlid.isNull());
}
void ULIDTest::setUp()
{
}
void ULIDTest::tearDown()
{
}
CppUnit::Test* ULIDTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ULIDTest");
CppUnit_addTest(pSuite, ULIDTest, testParse);
CppUnit_addTest(pSuite, ULIDTest, testBuffer);
CppUnit_addTest(pSuite, ULIDTest, testCompare);
CppUnit_addTest(pSuite, ULIDTest, testSwap);
CppUnit_addTest(pSuite, ULIDTest, testTryParse);
return pSuite;
}

View File

@@ -0,0 +1,42 @@
//
// ULIDTest.h
//
// Definition of the ULIDTest class.
//
// Copyright (c) 2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ULIDTest_INCLUDED
#define ULIDTest_INCLUDED
#include "Poco/Foundation.h"
#include "CppUnit/TestCase.h"
class ULIDTest: public CppUnit::TestCase
{
public:
ULIDTest(const std::string& name);
~ULIDTest();
void testParse();
void testBuffer();
void testCompare();
void testSwap();
void testTryParse();
void setUp();
void tearDown();
static CppUnit::Test* suite();
private:
};
#endif // ULIDTest_INCLUDED

View File

@@ -0,0 +1,24 @@
//
// ULIDTestSuite.cpp
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "ULIDTestSuite.h"
#include "ULIDTest.h"
#include "ULIDGeneratorTest.h"
CppUnit::Test* ULIDTestSuite::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ULIDTestSuite");
pSuite->addTest(ULIDTest::suite());
pSuite->addTest(ULIDGeneratorTest::suite());
return pSuite;
}

View File

@@ -0,0 +1,27 @@
//
// ULIDTestSuite.h
//
// Definition of the ULIDTestSuite class.
//
// Copyright (c) 2025, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ULIDTestSuite_INCLUDED
#define ULIDTestSuite_INCLUDED
#include "CppUnit/TestSuite.h"
class ULIDTestSuite
{
public:
static CppUnit::Test* suite();
};
#endif // ULIDTestSuite_INCLUDED

View File

@@ -14,6 +14,7 @@
#include "Poco/UUID.h"
#include "Poco/Exception.h"
UUIDTest::UUIDTest(const std::string& name): CppUnit::TestCase(name)
{
}