Пример #1
0
        /// <summary>
        /// Creates and returns random bytes based on internal <see cref="IUlidRng"/>.
        /// </summary>
        /// <param name="dateTime">
        /// DateTime for which the random bytes need to be generated; this value is used to determine wether a sequence
        /// needs to be incremented (same timestamp with millisecond resolution) or reset to a new random value.
        /// </param>
        /// <returns>Random bytes.</returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown when the specified <paramref name="dateTime"/> is before the last time this method was called.
        /// </exception>
        public override byte[] GetRandomBytes(DateTimeOffset dateTime)
        {
            lock (_genlock)
            {
                // Get unix time for given datetime
                var timestamp = Ulid.ToUnixTimeMilliseconds(dateTime);

                if (timestamp <= _lastgen)  // Same or earlier timestamp as last time we generated random values?
                {
                    // Increment our random value by one.
                    var i = RANDLEN;
                    while (--i >= 0 && ++_lastvalue[i] == 0)
                    {
                        ;
                    }
                    // If i made it all the way to -1 we have an overflow and we throw
                    if (i < 0)
                    {
                        throw new OverflowException();
                    }
                }
                else // New(er) timestamp, so generate a new random value and store the new(er) timestamp
                {
                    _lastvalue    = _rng.GetRandomBytes(dateTime);              // Use internal RNG to get bytes from
                    _lastvalue[0] = (byte)(_lastvalue[0] & 0x7F); // Mask out bit 0 of the random part

                    _lastgen = timestamp;                         // Store last timestamp
                }
                return(_lastvalue);
            }
        }
Пример #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MonotonicUlidRng"/> class.
        /// </summary>
        /// <param name="rng">The <see cref="IUlidRng"/> to get the random numbers from.</param>
        /// <param name="lastValue">The last value to 'continue from'; use <see langword="null"/> for defaults.</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="rng"/> is null.</exception>
        public MonotonicUlidRng(IUlidRng rng, Ulid?lastValue = null)
        {
            _rng = rng ?? throw new ArgumentNullException(nameof(rng));

            _lastvalue = lastValue == null ? new byte[RANDLEN] : lastValue.Value.Random;
            _lastgen   = Ulid.ToUnixTimeMilliseconds(lastValue == null ? Ulid.EPOCH : lastValue.Value.Time);
        }
Пример #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MonotonicUlidRng"/> class.
        /// </summary>
        /// <param name="rng">The <see cref="IUlidRng"/> to get the random numbers from.</param>
        /// <param name="maskMsbBits">
        /// The number of (most significant) bits to mask out / set to 0 when generating a random value for a given
        /// timestamp
        /// </param>
        /// <param name="lastValue">The last value to 'continue from'; use <see langword="null"/> for defaults.</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="rng"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown when <paramref name="maskMsbBits"/> is less than 0 or more than 70.
        /// </exception>
        public MonotonicUlidRng(IUlidRng rng, int maskMsbBits = DEFAULTMSBMASKBITS, Ulid?lastValue = null)
        {
            _rng = rng ?? throw new ArgumentNullException(nameof(rng));
            var maskbits = maskMsbBits >= 0 && maskMsbBits <= 80 - DEFAULTMSBMASKBITS ? maskMsbBits : throw new ArgumentOutOfRangeException(nameof(maskMsbBits));

            _lastvalue = lastValue == null ? new byte[RANDLEN] : lastValue.Value.Random;
            _lastgen   = Ulid.ToUnixTimeMilliseconds(lastValue == null ? Ulid.EPOCH : lastValue.Value.Time);

            // Prepare (or 'pre-compute') mask
            for (var i = 0; i < _mask.Length; i++)
            {
                var bits = maskbits > 8 ? 8 : maskbits; // Calculate number of bits to mask from this byte
                maskbits -= bits;                       // Decrease number of bits needing to mask total
                _mask[i]  = (byte)((1 << 8 - bits) - 1);
            }
        }
Пример #4
0
        /// <summary>
        /// Creates and returns random bytes based on internal <see cref="IUlidRng"/>.
        /// </summary>
        /// <param name="dateTime">
        /// DateTime for which the random bytes need to be generated; this value is used to determine wether a sequence
        /// needs to be incremented (same timestamp with millisecond resolution) or reset to a new random value.
        /// </param>
        /// <returns>Random bytes.</returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown when the specified <paramref name="dateTime"/> is before the last time this method was called.
        /// </exception>
        public override byte[] GetRandomBytes(DateTimeOffset dateTime)
        {
            lock (_genlock)
            {
                // Get unix time for given datetime
                var timestamp = Ulid.ToUnixTimeMilliseconds(dateTime);

                if (timestamp < _lastgen)
                {
                    throw new InvalidOperationException("Clock moved backwards; this is not supported.");
                }

                if (timestamp == _lastgen)  // Same timestamp as last time we generated random values?
                {
                    // Increment our random value by one.
                    var i = RANDLEN;
                    while (--i >= 0 && ++_lastvalue[i] == 0)
                    {
                        ;
                    }
                    // If i made it all the way to -1 we have an overflow and we throw
                    if (i < 0)
                    {
                        throw new OverflowException();
                    }
                }
                else // New(er) timestamp, so generate a new random value and store the new(er) timestamp
                {
                    _lastvalue = _rng.GetRandomBytes(dateTime);                 // Use internal RNG to get bytes from
                    for (var i = 0; i < _mask.Length && _mask[i] < 255; i++)    // Mask out desired number of MSB's
                    {
                        _lastvalue[i] = (byte)(_lastvalue[i] & _mask[i]);
                    }

                    _lastgen = timestamp;   // Store last timestamp
                }
                return(_lastvalue);
            }
        }