#### libremiliacr
#### Copyright(C) 2020-2024 Remilia Scarlet <remilia@posteo.jp>
####
#### This program is free software: you can redistribute it and/or modify
#### it under the terms of the GNU General Public License as published
#### the Free Software Foundation, either version 3 of the License, or
#### (at your option) any later version.
####
#### This program is distributed in the hope that it will be useful,
#### but WITHOUT ANY WARRANTY; without even the implied warranty of
#### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
#### GNU General Public License for more details.
####
#### You should have received a copy of the GNU General Public License
#### along with this program.If not, see<http:####www.gnu.org/licenses/.>
require "math"
require "big"
####
#### Extensions to the standard library.
####
# A convenience macro that is the as doing `(thing & flag) != 0`, but slightly
# cleaner looking.
macro bitflag?(thing, flag)
(({{thing}} & {{flag}}) != 0)
end
struct Time
UNIVERSAL_TIME_OFFSET = 2_208_988_800
# Returns the number of seconds since the beginning of the year 1900.
#
# [See the Common Lisp documentation on Universal Time](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_u.htm#universal_time)
def toUniversal : Int64
self.to_unix + UNIVERSAL_TIME_OFFSET
end
# Creates a new `Time` instance from the given universal time.
#
# [See the Common Lisp documentation on Universal Time](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_u.htm#universal_time)
def self.universal(seconds : Int64) : Time
self.unix(universalToUnix(seconds))
end
# Converts a universal time to a Unix time.
#
# [See the Common Lisp documentation on Universal Time](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_u.htm#universal_time)
def self.universalToUnix(sec : Int64) : Int64
sec - UNIVERSAL_TIME_OFFSET
end
# Converts a Unix time to a Universal time.
#
# [See the Common Lisp documentation on Universal Time](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_u.htm#universal_time)
def self.unixToUniversal(sec : Int64) : Int64
sec + UNIVERSAL_TIME_OFFSET
end
end
class ::Array(T)
# Returns a new `Slice` that contains the same elements as this array.
def toSlice : Slice(T)
ret = Slice(T).new(self.size)
self.to_unsafe.copy_to(ret.to_unsafe, self.size)
ret
end
end
struct ::Slice(T)
# Returns a new `Array` that contains the same elements as this slice.
def toArray : Array(T)
Array(T).new(self.size) do |i|
self.[i]
end
end
# Similar to `realloc()` in C. This returns a new `Slice` with the new
# requested size. The contents of `self` are copied over to the new `Slice`.
# If the new `Slice` is larger than `self`, then the new elements are set to
# `default`. If the new `Slice` is smaller, then only as many items as will
# fit are copied over.
#
# Internally, this is equivalent to either using `Slice#[range]` when the new
# size is smaller, `Slice#dup` when the sizes are the same, and
# `Slice#initialize` + `Slice#copy_from` when the new size is larger.
def realloc(newSize : Int32, default : T) : self
if newSize < self.size
# Just return a new slice
self[0...newSize]
elsif newSize == self.size
# Copy
self.dup
else
# Larger than self
ret = Slice(T).new(newSize, default)
ret.copy_from(self)
ret
end
end
end
abstract class ::IO
# Temporarily changes the stream position to `gotoHere`, then yields. This
# automatically returns to the position before calling this method before it
# returns.
#
# This only works for types that can get and set their position.
def withExcursion(gotoHere, &)
oldPos = self.pos
self.pos = gotoHere
yield
self.pos = oldPos
end
# Yields, then automatically returns to the position before calling this
# method before it returns.
#
# This only works for types that can get and set their position.
def withExcursion(&)
oldPos = self.pos
yield
self.pos = oldPos
end
# Reads *count* bytes into a `::Bytes` and returns the new `::Bytes`. If
# *count* bytes could not be read, this will return a `::Bytes` that is equal
# in length to the number of bytes actually read.
def self.readBytes(io : IO, count : Int) : Bytes
raise ::ArgumentError.new("Bad byte count") if count < 0
ret = Bytes.new(count)
numRead = io.read(ret)
if numRead == count
ret
else
ret[0...numRead]
end
end
# :ditto:
@[AlwaysInline]
def readBytes(count : Int) : Bytes
IO.readBytes(self, count)
end
# Reads a 24-bit signed little-endian integer from `io`.
@[AlwaysInline]
def self.readInt24(io : IO) : Int32
b1 = io.read_byte.try &.to_i32! || raise IO::EOFError.new("Could not read the first byte of a 24-bit int")
b2 = io.read_byte.try &.to_i32! || raise IO::EOFError.new("Could not read the second byte of a 24-bit int")
b3 = io.read_byte.try &.to_i32! || raise IO::EOFError.new("Could not read the third byte of a 24-bit int")
ret : Int32 = b3 << 16 | b2 << 8 | b1
ret |= ~0x7FFFFF if ret & 0x800000 != 0
ret
end
# Reads a 24-bit signed big-endian integer from `io`.
@[AlwaysInline]
def self.readInt24BE(io : IO) : Int32
b3 = io.read_byte.try &.to_i32! || raise IO::EOFError.new("Could not read the first byte of a 24-bit int")
b2 = io.read_byte.try &.to_i32! || raise IO::EOFError.new("Could not read the second byte of a 24-bit int")
b1 = io.read_byte.try &.to_i32! || raise IO::EOFError.new("Could not read the third byte of a 24-bit int")
ret : Int32 = b3 << 16 | b2 << 8 | b1
ret |= ~0x7FFFFF if ret & 0x800000 != 0
ret
end
# Reads a 24-bit signed little-endian integer from `io`.
@[AlwaysInline]
def readInt24
IO.readInt24(self)
end
# Reads a 24-bit signed big-endian integer from `io`.
@[AlwaysInline]
def readInt24BE
IO.readInt24BE(self)
end
# Writes `data` as a 24-bit signed little-endian integer. This does not check
# to see if `data` is within the range of a 24-bit integer and always writes
# it as 24-bits.
@[AlwaysInline]
def writeInt24(data : Int32)
write_byte((data & 0x0000FF).to_u8!)
write_byte(((data & 0x00FF00) >> 8).to_u8!)
write_byte(((data & 0xFF0000) >> 16).to_u8!)
end
# Writes `data` as a 24-bit signed big-endian integer. This does not check to
# see if `data` is within the range of a 24-bit integer and always writes it
# as 24-bits.
@[AlwaysInline]
def writeInt24BE(data : Int32)
write_byte(((data & 0xFF0000) >> 16).to_u8!)
write_byte(((data & 0x00FF00) >> 8).to_u8!)
write_byte((data & 0x0000FF).to_u8!)
end
###
### Convenience functions.
###
### These can all be accomplished with the standard library, but these names
### are a bit shorter to type.
###
# Convenience method to read a signed 8-bit byte from `io`. If the byte
# cannot be read, this will raise an `IO::EOFError`.
#
# This is the same as calling `io.read_byte.try &.to_i8! || raise IO::EOFError.new`,
# just shorter.
@[AlwaysInline]
def self.readInt8(io : IO) : Int8
io.read_byte.try &.to_i8! || raise IO::EOFError.new
end
# Convenience method to read a signed 8-bit byte. If the byte cannot be read,
# this will raise an `IO::EOFError`.
#
# This is the same as calling `io.read_byte.try &.to_i8! || raise IO::EOFError.new`,
# just shorter.
@[AlwaysInline]
def readInt8
IO.readInt8(self)
end
# Convenience method to write a signed 8-bit byte to `io`.
#
# This is the same as calling `io.write_byte(value.to_u8!)`, just shorter.
@[AlwaysInline]
def self.writeInt8(io : IO, value : Int8) : Nil
io.write_byte(value.to_u8!)
end
# Convenience method to write a signed 8-bit byte.
#
# This is the same as calling `io.write_byte(value.to_u8!)`, just shorter.
@[AlwaysInline]
def writeInt8(value : Int8) : Nil
self.write_byte(value.to_u8!)
end
# Convenience method to read an unsigned 8-bit byte from `io`. If the byte
# cannot be read, this will raise an `IO::EOFError`.
#
# This is the same as calling `io.read_byte || raise IO::EOFError.new`, just
# shorter.
@[AlwaysInline]
def self.readUInt8(io : IO) : UInt8
io.read_byte || raise IO::EOFError.new
end
# Convenience method to read an unsigned 8-bit byte. If the byte cannot be
# read, this will raise an `IO::EOFError`.
#
# This is the same as calling `io.read_byte || raise IO::EOFError.new`, just
# shorter.
@[AlwaysInline]
def readUInt8
IO.readUInt8(self)
end
# Convenience method to write an unsigned 8-bit byte to `io`.
#
# This is the same as calling `io.write_byte(value)` and is included for
# consistency.
@[AlwaysInline]
def self.writeUInt8(io : IO, value : UInt8) : Nil
io.write_byte(value)
end
# Convenience method to write an unsigned 8-bit byte.
#
# This is the same as calling `io.write_byte(value)` and is included
# for consistency.
@[AlwaysInline]
def writeUInt8(value : UInt8) : Nil
self.write_byte(value)
end
{% begin %}
{% classes = [Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128] %}
{% bits = [16, 16, 32, 32, 64, 64, 128, 128] %}
{% for i in 0...classes.size %}
# Convenience method to read a signed {{bits[i]}}-bit little-endian integer from `io`.
#
# This is the same as calling `io.read_bytes({{classes[i]}}, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def self.read{{classes[i].id}}(io : IO) : {{classes[i].id}}
io.read_bytes({{classes[i].id}}, IO::ByteFormat::LittleEndian)
end
# Convenience method to read a signed {{bits[i]}}-bit big-endian integer from `io`.
#
# This is the same as calling `io.read_bytes({{classes[i].id}}, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def self.read{{classes[i].id}}BE(io : IO) : {{classes[i].id}}
io.read_bytes({{classes[i].id}}, IO::ByteFormat::BigEndian)
end
# Convenience method to read a signed {{bits[i]}}-bit little-endian integer.
#
# This is the same as calling `read_bytes({{classes[i].id}}, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def read{{classes[i].id}}
IO.read{{classes[i].id}}(self)
end
# Convenience method to read a signed {{bits[i]}}-bit big-endian integer.
#
# This is the same as calling `read_bytes({{classes[i].id}}, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def read{{classes[i].id}}BE
IO.read{{classes[i].id}}BE(self)
end
# Convenience method to write a signed {{bits[i]}}-bit little-endian integer. This returns `self.
#
# This is the same as calling `write_bytes(value, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def write{{classes[i].id}}(value : {{classes[i].id}}) : self
self.write_bytes(value, IO::ByteFormat::LittleEndian)
self
end
# Convenience method to write a signed {{bits[i]}}-bit big-endian integer. This returns `self.
#
# This is the same as calling `write_bytes(value, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def write{{classes[i].id}}BE(value : {{classes[i].id}}) : self
self.write_bytes(value, IO::ByteFormat::BigEndian)
self
end
{% end %}
{% end %}
# Convenience method to write a signed 32-bit little-endian float. This returns `self.
#
# This is the same as calling `write_bytes(value, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def writeFloat32(value : Float32) : self
self.write_bytes(value, IO::ByteFormat::LittleEndian)
self
end
# Convenience method to write a signed 64-bit little-endian float. This returns `self.
#
# This is the same as calling `write_bytes(value, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def writeFloat64(value : Float64) : self
self.write_bytes(value, IO::ByteFormat::LittleEndian)
self
end
# Convenience method to write a signed 32-bit big-endian float. This returns `self.
#
# This is the same as calling `write_bytes(value, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def writeFloat32BE(value : Float32) : self
self.write_bytes(value, IO::ByteFormat::BigEndian)
self
end
# Convenience method to write a signed 64-bit big-endian float. This returns `self.
#
# This is the same as calling `write_bytes(value, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def writeFloat64BE(value : Float64) : self
self.write_bytes(value, IO::ByteFormat::BigEndian)
self
end
# Convenience method to read a 32-bit little-endian floating point number from `io`.
#
# This is the same as calling `io.read_bytes(Float32, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def self.readFloat32(io : IO) : Float32
io.read_bytes(Float32, IO::ByteFormat::LittleEndian)
end
# Convenience method to read a 32-bit big-endian floating point number from `io`.
#
# This is the same as calling `io.read_bytes(Float32, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def self.readFloat32BE(io : IO) : Float32
io.read_bytes(Float32, IO::ByteFormat::BigEndian)
end
# Convenience method to read a 32-bit little-endian floating point number.
#
# This is the same as calling `io.read_bytes(Float32, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def readFloat32
IO.readFloat32(self)
end
# Convenience method to read a 32-bit big-endian floating point number.
#
# This is the same as calling `io.read_bytes(Float32, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def readFloat32BE
IO.readFloat32BE(self)
end
# Convenience method to read a 64-bit little-endian floating point number from `io`.
#
# This is the same as calling `io.read_bytes(Float64, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def self.readFloat64(io : IO) : Float64
io.read_bytes(Float64, IO::ByteFormat::LittleEndian)
end
# Convenience method to read a 64-bit big-endian floating point number from `io`.
#
# This is the same as calling `io.read_bytes(Float64, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def self.readFloat64BE(io : IO) : Float64
io.read_bytes(Float64, IO::ByteFormat::LittleEndian)
end
# Convenience method to read a 64-bit little-endian floating point number.
#
# This is the same as calling `io.read_bytes(Float64, IO::ByteFormat::LittleEndian)`,
# just shorter.
@[AlwaysInline]
def readFloat64
IO.readFloat64(self)
end
# Convenience method to read a 64-bit big-endian floating point number.
#
# This is the same as calling `io.read_bytes(Float64, IO::ByteFormat::BigEndian)`,
# just shorter.
@[AlwaysInline]
def readFloat64BE
IO.readFloat64BE(self)
end
end
module RemiLib
# :nodoc:
PRETTY_SIZE_SUFFIXES = [
"Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
]
PRETTY_SIZE_SUFFIXES_SHORT = [
"B", "K", "M", "G", "T", "P", "E", "Z", "Y"
]
end
struct ::Int
# Similar to `Int#humanize_bytes`, except that this always uses
# `Int::BinaryPrefixFormat::JEDEC` for the format, and formats numbers
# slightly differently.
#
# If `alwaysShowAsBytes` is true, then this will always show the size as
# bytes.
#
# `separator` is used for the decimal separator. `delimiter` is used only
# when `alwaysShowAsBytes` is true, and is used as the thousands delimiter.
#
# `padding` instances of `padChar` will be inserted to the left of the
# formatted size if `padding` is positive. If it's negative, then that many
# instances of `padChar` will be inserted to the right of the formatted size.
def prettySize(*, alwaysShowAsBytes : Bool = false, decimalPlaces : Int = 2, separator : Char = '.',
delimiter : Char = ',', padding : Int = 0, padChar : Char = ' ', shortSuffix : Bool = false) : String
String.build do |str|
self.prettySize(str, alwaysShowAsBytes: alwaysShowAsBytes, decimalPlaces: decimalPlaces, separator: separator,
delimiter: delimiter, padding: padding, padChar: padChar,
shortSuffix: shortSuffix)
end
end
def prettySize(io : IO, *, alwaysShowAsBytes : Bool = false, decimalPlaces : Int = 2, separator : Char = '.',
delimiter : Char = ',', padding : Int = 0, padChar : Char = ' ', shortSuffix : Bool = false) : Nil
if padding > 0
padding.times { |_| io << padChar }
end
if alwaysShowAsBytes
self.format(io, separator, delimiter, only_significant: false)
io << (shortSuffix ? " B" : " Bytes")
else
mag : Int128 = if self > 0
Math.log(self, 1024).to_i128!
elsif self < 0
Math.log(self.abs, 1024).to_i128!
else
0i128
end
humanSize : Float64 = if self > 0
self.to_i128! / (1u128 << (mag * 10))
elsif self < 0
-(self.abs.to_i128! / (1u128 << (mag * 10)))
else
0.0f64
end
if mag == 0
self.format(io, separator, delimiter, only_significant: false)
io << (shortSuffix ? " B" : " Bytes")
elsif decimalPlaces == 0
humanSize.to_i.format(io, separator, delimiter, only_significant: true)
io << ' ' << (shortSuffix ? RemiLib::PRETTY_SIZE_SUFFIXES_SHORT[mag] :
RemiLib::PRETTY_SIZE_SUFFIXES[mag])
else
humanSize.format(io, separator, delimiter, decimalPlaces, only_significant: false)
io << ' ' << (shortSuffix ? RemiLib::PRETTY_SIZE_SUFFIXES_SHORT[mag] :
RemiLib::PRETTY_SIZE_SUFFIXES[mag])
end
end
if padding < 0
padding.abs.times { |_| io << padChar }
end
end
ROMAN_NUMERALS = [
{1000, "M"},
{900, "CM"},
{500, "D"},
{400, "CD"},
{100, "C"},
{90, "XC"},
{50, "L"},
{40, "XL"},
{10, "X"},
{9, "IX"},
{5, "V"},
{4, "IV"},
{1, "I"}]
ENGLISH_NUMBERS = {
0 => "zero",
1 => "one",
2 => "two",
3 => "three",
4 => "four",
5 => "five",
6 => "six",
7 => "seven",
8 => "eight",
9 => "nine",
10 => "ten",
11 => "eleven",
12 => "twelve",
13 => "thirteen",
14 => "fourteen",
15 => "fifteen",
16 => "sixteen",
17 => "seventeen",
18 => "eighteen",
19 => "nineteen",
20 => "twenty",
30 => "thirty",
40 => "forty",
50 => "fifty",
60 => "sixty",
70 => "seventy",
80 => "eighty",
90 => "ninety"
}.to_h
# Converts this number to a Roman numeral. Only values between 1
# and 3999, inclusive, are supported, otherwise an `ArgumentError`
# is raised.
def toRoman : String
unless self > 0 && self < 4000
raise ArgumentError.new("Cannot represent number as a roman numeral: #{self}")
end
self.toRoman? || raise "Unexpected nil from #toRoman?"
end
# Converts this number to a Roman numeral. Only values between 1
# and 3999, inclusive, are supported, otherwise this returns `nil`.
def toRoman? : String?
# Adapted from https://github.com/jkfurtney/clformat
unless self > 0 && self < 4000
return nil
end
i = self
String.build do |str|
ROMAN_NUMERALS.each do |num|
count = i // num[0]
str << num[1] * count
i -= num[0] * count
end
end
end
# :nodoc:
ONE_DUODECILLION = BigInt.new("1000000000000000000000000000000000000000")
# :nodoc:
ONE_TREDECILLION = BigInt.new("1000000000000000000000000000000000000000000")
# Converts a number into a string such that it appears as English words. For
# example, 100 would return `"one hundred"`.
@[AlwaysInline]
def positiveSpokenNumber : String
Int.positiveSpokenNumber(self)
end
# :ditto:
def self.positiveSpokenNumber(num) : String
# Adapted from https://github.com/jkfurtney/clformat
# Try to early out
return ENGLISH_NUMBERS[num] if ENGLISH_NUMBERS[num]?
raise "ENGLISH_NUMBERS check failed" if num <= 20
rem = 0
String.build do |str|
{% begin %}
case
when num < 100
str << positiveSpokenNumber((num // 10) * 10) << '-' << positiveSpokenNumber(num % 10)
{% if @type != Int8 && @type != UInt8 %}
when num < 1000
str << positiveSpokenNumber(num // 100) << ' ' << "hundred "
rem = num % 100
when num < 1000000
str << positiveSpokenNumber(num // 1000) << ' ' << "thousand"
rem = num % 1000
when num < 1000000000
str << positiveSpokenNumber(num // 1000000) << ' ' << "million"
rem = num % 1000000
when num < 1000000000000
str << positiveSpokenNumber(num // 1000000000) << ' ' << "billion"
rem = num % 1000000000
{% if @type != Int16 && @type != UInt16 %}
when num < 1000000000000000
str << positiveSpokenNumber(num // 1000000000000) << ' ' << "trillion"
rem = num % 1000000000000
when num < 1000000000000000000
str << positiveSpokenNumber(num // 1000000000000000) << ' ' << "quadrillion"
rem = num % 1000000000000000
when num < 1000000000000000000000_i128
str << positiveSpokenNumber(num // 1000000000000000000_i128) << ' ' << "quintillion"
rem = num % 1000000000000000000_i128
{% if @type != Int32 && @type != UInt32 %}
when num < 1000000000000000000000000_i128
str << positiveSpokenNumber(num // 1000000000000000000000_i128) << ' ' << "sextillion"
rem = num % 1000000000000000000000_i128
when num < 1000000000000000000000000000_i128
str << positiveSpokenNumber(num // 1000000000000000000000000_i128) << ' ' << "septillion"
rem = num % 1000000000000000000000000_i128
when num < 1000000000000000000000000000000_i128
str << positiveSpokenNumber(num // 1000000000000000000000000000_i128) << ' ' << "octillion"
rem = num % 1000000000000000000000000000_i128
when num < 1000000000000000000000000000000000_i128
str << positiveSpokenNumber(num // 1000000000000000000000000000000_i128) << ' ' << "nonillion"
rem = num % 1000000000000000000000000000000_i128
when num < 1000000000000000000000000000000000000_i128
str << positiveSpokenNumber(num // 1000000000000000000000000000000000_i128) << ' ' << "decillion"
rem = num % 1000000000000000000000000000000000_i128
{% if @type != Int64 && @type != UInt64 %}
when num < ONE_DUODECILLION
str << positiveSpokenNumber(num // 1000000000000000000000000000000000000_i128) << ' ' << "undecillion"
rem = num % 1000000000000000000000000000000000000_i128
{% if BigFloat.has_method?(:to_i128) %}
when num < ONE_TREDECILLION
str << positiveSpokenNumber(num // ONE_DUODECILLION) << ' ' << "duodecillion"
rem = num % ONE_DUODECILLION
{% end %}
{% end %}
{% end %}
{% end %}
{% end %}
else
raise ArgumentError.new("Number cannot be represented as an English number")
end
{% end %}
unless rem == 0
str << (rem >= 100 ? ", " : "") << positiveSpokenNumber(rem)
end
end
end
def toSpoken : String
{% begin %}
{% if @type != UInt8 && @type != UInt16 && @type != UInt32 && @type != UInt64 && @type != UInt128 %}
if self < 0
return "minus #{Int.positiveSpokenNumber(-self)}"
end
{% end %}
{% end %}
Int.positiveSpokenNumber(self)
end
end
{% begin %}
{% classes = [::Int8, ::UInt8, ::Int16, ::UInt16, ::Int32, ::UInt32, ::Int64, ::UInt64, ::Int128, ::UInt128] %}
{% bitSizes = [8, 8, 16, 16, 32, 32, 64, 64, 128, 128] %}
{% for i in 0...classes.size %}
struct {{classes[i]}}
# Returns the number of zero bits in the number before a 1 bit is encountered.
def numLeadingZeros : Int
total = 0
{{bitSizes[i] - 1}}.downto(0) do |i|
if self.bit(i) == 0
total += 1
else
break
end
end
total
end
# Returns the number of one bits in the number before a 0 bit is encountered.
def numLeadingOnes : Int
total = 0
{{bitSizes[i] - 1}}.downto(0) do |i|
if self.bit(i) == 1
total += 1
else
break
end
end
total
end
end
{% end %}
{% end %}
{% begin %}
{% classes = [::Int8, ::UInt8, ::Int16, ::UInt16, ::Int32, ::UInt32, ::Int64, ::UInt64, ::Int128, ::UInt128] %}
{% maskSuffixes = [:_u8, :_u8, :_u16, :_u16, :_u32, :_u32, :_u64, :_u64, :_u128, :_u128] %}
{% retClasses = [Union(::Int8, ::UInt8), Union(::Int16, ::UInt16), Union(::Int32, ::UInt32),
Union(::Int64, ::UInt64), Union(::Int128, ::UInt128)] %}
{% bitSizes = [8, 8, 16, 16, 32, 32, 64, 64, 128, 128] %}
{% for i in 0...classes.size %}
struct {{classes[i]}}
# Converts this integer to a number that is `bits` bits wide. If
# `asUnsigned` is `true`, then the converted value is converted into an
# unsigned integer of `bits` size, otherwise it is converted to a
# twos-complement signed integer of `bits` size.
#
# `bits` must be at least 1. If `bits` is greater than or equal to the
# size of `self` in bits, then `self` is simply returned (possibly
# converted to an unsigned version if `asUnsigned` is `true`).
def asBitSize(bits : Int, asUnsigned : Bool = false) : {{retClasses[i // 2]}}
# Check the requested bit size.
if bits <= 0
raise ArgumentError.new("Bad bit size for an integer of #{ {{bitSizes[i]}} } bits")
elsif bits >= {{bitSizes[i]}}
if asUnsigned
return self.to_u{{bitSizes[i]}}!
else
return self.to_i{{bitSizes[i]}}!
end
end
# (logand x (logior x (- (mask-field (byte 1 (1- bit-size)) x))))
mask = ((2{{maskSuffixes[i].id}} << (bits - 1)) - 1)
ret = self.to_u{{bitSizes[i]}}! & mask
if asUnsigned
ret
else
if ret.bit(bits - 1) == 0
ret.to_i{{bitSizes[i]}}
else
retMask = -{{bitSizes[i]}}.to_u{{bitSizes[i]}}! & ~mask
(ret | retMask).to_i{{bitSizes[i]}}!
end
end
end
end
{% end %}
{% end %}
class File
# Creates a file at *path* that is *size* bytes large, filled entirely with
# zeros.
def self.createEmpty(path : String|Path, size : Int) : Nil
File.open(path, "wb") do |file|
if size > 0
file.seek((size - 1).to_i64, File::Seek::Set)
file.write_byte(0)
file.flush
end
end
end
end
{% begin %}
{% if compare_versions(Crystal::VERSION, "1.7.0") < 0 %}
{% puts "Monkey patching byte_swap into the integer classes" %}
{% classes = [::Int16, ::UInt16, ::Int32, ::UInt32, ::Int64, ::UInt64, ::Int128, ::UInt128] %}
{% sizes = [2, 2, 4, 4, 8, 8, 16, 16] %}
{% for klass in 0...classes.size %}
struct {{classes[klass]}}
# Swaps the bytes of self; a little-endian value becomes a big-endian
# value, and vice-versa. The bit order within each byte is unchanged.
def byte_swap : self
{% for i in 0...sizes[klass] %}
b{{i}} = (self & {{0xFF << (8 * i)}}) >> {{8 * i}}
{% end %}
{% for i in 0...sizes[klass] %}
(b{{i}} << {{8 * ((sizes[klass] - 1) - i)}}) |
{% end %}
0
end
end
{% end %}
{% end %}
{% end %}