- Joined
- Nov 7, 2014
- Messages
- 571
BitBuf - packs integers into a bit buffer -> encodes to base64 -> decodes from base64 -> unpacks integers from a bit buffer
Edit: the destroy method (which did nothing previously) is now disabled (panics when called) because deallocating the memory used by a
Edit2: the destroy method should work correctly now (apparently it wasn't that hard after all =))
JASS:
library BitBuf requires Bitops
//! novjass
BitBuf - packs integers into a bit buffer -> encodes to base64 -> decodes from base64 -> unpacks integers from a bit buffer
requires:
Bitops: http://www.hiveworkshop.com/threads/bitops.274878/
API:
//
// Creating/Destroying a BitBuf
//
// Creates a BitBuf of size `size_in_bits` that can be written to.
//
static method create takes integer size_in_bits returns BitBuf
method destroy takes nothing returns nothing
// Creates a BitBuf from a base64 encoded string that can be read from.
//
static method from_base64 takes string base64 returns BitBuf
//
// Writing to a BitBuf
//
// Instead of creating and destroying a BitBuf it can be created/initialized
// once and be written to over and over again after calling this method.
method begin_writing takes nothing returns nothing
// BitBuf.from_base64 calls this method automatically.
// You can call it manually after writing to the BitBuf.
method begin_reading takes nothing returns nothing
// Writing unsigned (>= 0) integers.
// Note: the notation: a .. b denotes an inclusive on both ends range.
// Ranges are checked only in debug mode.
method write_u1 takes integer u1 returns nothing // range: 0 .. 1
method write_u2 takes integer u2 returns nothing // range: 0 .. 3
method write_u3 takes integer u3 returns nothing // range: 0 .. 7
method write_u4 takes integer u4 returns nothing // range: 0 .. 15
method write_u5 takes integer u5 returns nothing // range: 0 .. 31
method write_u6 takes integer u6 returns nothing // range: 0 .. 63
method write_u7 takes integer u7 returns nothing // range: 0 .. 127
method write_u8 takes integer u8 returns nothing // range: 0 .. 255
method write_u9 takes integer u9 returns nothing // range: 0 .. 511
method write_u10 takes integer u10 returns nothing // range: 0 .. 1023
method write_u11 takes integer u11 returns nothing // range: 0 .. 2047
method write_u12 takes integer u12 returns nothing // range: 0 .. 4095
method write_u13 takes integer u13 returns nothing // range: 0 .. 8191
method write_u14 takes integer u14 returns nothing // range: 0 .. 16383
method write_u15 takes integer u15 returns nothing // range: 0 .. 32767
method write_u16 takes integer u16 returns nothing // range: 0 .. 65535
method write_u17 takes integer u17 returns nothing // range: 0 .. 131071
method write_u18 takes integer u18 returns nothing // range: 0 .. 262143
method write_u19 takes integer u19 returns nothing // range: 0 .. 524287
method write_u20 takes integer u20 returns nothing // range: 0 .. 1048575
method write_u21 takes integer u21 returns nothing // range: 0 .. 2097151
method write_u22 takes integer u22 returns nothing // range: 0 .. 4194303
method write_u23 takes integer u23 returns nothing // range: 0 .. 8388607
method write_u24 takes integer u24 returns nothing // range: 0 .. 16777215
method write_u25 takes integer u25 returns nothing // range: 0 .. 33554431
method write_u26 takes integer u26 returns nothing // range: 0 .. 67108863
method write_u27 takes integer u27 returns nothing // range: 0 .. 134217727
method write_u28 takes integer u28 returns nothing // range: 0 .. 268435455
method write_u29 takes integer u29 returns nothing // range: 0 .. 536870911
method write_u30 takes integer u30 returns nothing // range: 0 .. 1073741823
method write_u31 takes integer u31 returns nothing // range: 0 .. 2147483647
// Note: there is no write_u32 because Jass has only signed 32 bit integers
// Writing signed integers
// Note: there is no write_s1 method
method write_s2 takes integer s2 returns nothing // range: -2 .. 1
method write_s3 takes integer s3 returns nothing // range: -4 .. 3
method write_s4 takes integer s4 returns nothing // range: -8 .. 7
method write_s5 takes integer s5 returns nothing // range: -16 .. 15
method write_s6 takes integer s6 returns nothing // range: -32 .. 31
method write_s7 takes integer s7 returns nothing // range: -64 .. 63
method write_s8 takes integer s8 returns nothing // range: -128 .. 127
method write_s9 takes integer s9 returns nothing // range: -256 .. 255
method write_s10 takes integer s10 returns nothing // range: -512 .. 511
method write_s11 takes integer s11 returns nothing // range: -1024 .. 1023
method write_s12 takes integer s12 returns nothing // range: -2048 .. 2047
method write_s13 takes integer s13 returns nothing // range: -4096 .. 4095
method write_s14 takes integer s14 returns nothing // range: -8192 .. 8191
method write_s15 takes integer s15 returns nothing // range: -16384 .. 16383
method write_s16 takes integer s16 returns nothing // range: -32768 .. 32767
method write_s17 takes integer s17 returns nothing // range: -65536 .. 65535
method write_s18 takes integer s18 returns nothing // range: -131072 .. 131071
method write_s19 takes integer s19 returns nothing // range: -262144 .. 262143
method write_s20 takes integer s20 returns nothing // range: -524288 .. 524287
method write_s21 takes integer s21 returns nothing // range: -1048576 .. 1048575
method write_s22 takes integer s22 returns nothing // range: -2097152 .. 2097151
method write_s23 takes integer s23 returns nothing // range: -4194304 .. 4194303
method write_s24 takes integer s24 returns nothing // range: -8388608 .. 8388607
method write_s25 takes integer s25 returns nothing // range: -16777216 .. 16777215
method write_s26 takes integer s26 returns nothing // range: -33554432 .. 33554431
method write_s27 takes integer s27 returns nothing // range: -67108864 .. 67108863
method write_s28 takes integer s28 returns nothing // range: -134217728 .. 134217727
method write_s29 takes integer s29 returns nothing // range: -268435456 .. 268435455
method write_s30 takes integer s30 returns nothing // range: -536870912 .. 536870911
method write_s31 takes integer s31 returns nothing // range: -1073741824 .. 1073741823
method write_s32 takes integer s32 returns nothing // range: -2147483648 .. 2147483647
// Writing reals
// Note: for these methods the ENABLE_REAL_READ_WRITE configuration variable must be set to true
// Chops the lower 16 bits of the mantisa first and then writes the remaning 16 to the bit buffer.
method write_r16 takes real r32 returns nothing
method write_r32 takes real r32 returns nothing
// Writing booleans
// Writies a single bit that's set to 1 if the boolean is true, 0 otherwise.
method write_bool takes boolean b returns nothing
//
// Reading from a BitBuf
//
// Note: if an attempt is made to read pass the end of the buffer the script execution stops and an
// error is shown (in debug mode only).
// Reading unsigned (>= 0) integers
method read_u1 takes nothing returns integer
method read_u2 takes nothing returns integer
method read_u3 takes nothing returns integer
method read_u4 takes nothing returns integer
method read_u5 takes nothing returns integer
method read_u6 takes nothing returns integer
method read_u7 takes nothing returns integer
method read_u8 takes nothing returns integer
method read_u9 takes nothing returns integer
method read_u10 takes nothing returns integer
method read_u11 takes nothing returns integer
method read_u12 takes nothing returns integer
method read_u13 takes nothing returns integer
method read_u14 takes nothing returns integer
method read_u15 takes nothing returns integer
method read_u16 takes nothing returns integer
method read_u17 takes nothing returns integer
method read_u18 takes nothing returns integer
method read_u19 takes nothing returns integer
method read_u20 takes nothing returns integer
method read_u21 takes nothing returns integer
method read_u22 takes nothing returns integer
method read_u23 takes nothing returns integer
method read_u24 takes nothing returns integer
method read_u25 takes nothing returns integer
method read_u26 takes nothing returns integer
method read_u27 takes nothing returns integer
method read_u28 takes nothing returns integer
method read_u29 takes nothing returns integer
method read_u30 takes nothing returns integer
method read_u31 takes nothing returns integer
// Reading signed ntegers
method read_s2 takes nothing returns integer
method read_s3 takes nothing returns integer
method read_s4 takes nothing returns integer
method read_s5 takes nothing returns integer
method read_s6 takes nothing returns integer
method read_s7 takes nothing returns integer
method read_s8 takes nothing returns integer
method read_s9 takes nothing returns integer
method read_s10 takes nothing returns integer
method read_s11 takes nothing returns integer
method read_s12 takes nothing returns integer
method read_s13 takes nothing returns integer
method read_s14 takes nothing returns integer
method read_s15 takes nothing returns integer
method read_s16 takes nothing returns integer
method read_s17 takes nothing returns integer
method read_s18 takes nothing returns integer
method read_s19 takes nothing returns integer
method read_s20 takes nothing returns integer
method read_s21 takes nothing returns integer
method read_s22 takes nothing returns integer
method read_s23 takes nothing returns integer
method read_s24 takes nothing returns integer
method read_s25 takes nothing returns integer
method read_s26 takes nothing returns integer
method read_s27 takes nothing returns integer
method read_s28 takes nothing returns integer
method read_s29 takes nothing returns integer
method read_s30 takes nothing returns integer
method read_s31 takes nothing returns integer
method read_s32 takes nothing returns integer
// Reading reals
// Note: for these methods the ENABLE_REAL_READ_WRITE configuration variable must be set to true
method read_r16 takes nothing returns real
method read_r32 takes nothing returns real
// Reading booleans
// Reads a single bit if it's 1 returns true, false otherwise
method read_bool takes nothing returns boolean
//
// Encoding to base64
//
// Encodes the bits that were previously written to the buffer as a base64 string.
//
method to_base64 takes nothing returns string
// Applies a color to each class of digit
//
// uppercase letters: A .. Z, color: uc
// lowercase letters: a .. z, color: lc
// arabic numerals: 0 .. 9, color d
// '+' and '/': color p
//
static method base64_apply_colors takes string base64, string uc, string lc, string d, string p returns string
//
// Simple checksum
//
// Generates a check[sum] digit using the Luhn mod N algorithm
//
static method luhn_generate_check_digit takes string base64 returns string
// Validates the base64 encoded string against its check digit (last character)
//
static method luhn_is_valid takes string base64 returns boolean
//! endnovjass
globals
// In order to write/read reals we need to type cast them to integers first
// but this type casting is done by "tricking" the VM with a pretty clever trick
// but it might get fixed in upcoming patches (current version is: 1.27.0).
// Besides, reals take a lot of bits which could be saved for other things if the encoding is "smart".
private constant boolean ENABLE_REAL_READ_WRITE = false
endglobals
static if ENABLE_REAL_READ_WRITE then
//# +nosemanticerror
private function r2i takes real r returns integer
return r
endfunction
private function clean_int takes integer i returns integer
return i
return 0
endfunction
//# +nosemanticerror
private function i2r takes integer i returns real
return i
endfunction
private function clean_real takes real r returns real
return r
return 0.0
endfunction
public function real_as_int takes real r returns integer
return clean_int(r2i(r))
endfunction
public function int_as_real takes integer i returns real
return clean_real(i2r(i))
endfunction
endif
public function ceil takes real r returns integer
local integer i = R2I(r)
if r == i then
return i
endif
if r > 0.0 then
return i + 1
else // if r < 0 then
return i
endif
endfunction
public function round_up_nearest takes integer n, integer m returns integer
return ceil(n / I2R(m)) * m
endfunction
public function size_in_bits_to_size_in_words takes integer size_in_bits returns integer
return round_up_nearest(size_in_bits, 32) / 32
endfunction
private function panic takes string msg returns nothing
local integer i = 0
call BJDebugMsg("|cffFF0000BitBuf error: " + msg + "|r")
set i = 1 / 0 // we report the error and now we stop the script from running any further
endfunction
struct BitBuf extends array
private static integer bit_buf_alloc = 0
// heads of the free lists of BitBuf(s):
// heads[1] -- the head of the linked of BitBuf(s) with size_in_words = 1
// heads[2] -- ... with size_in_words = 2
// ...
private static thistype array heads
private thistype next
private static string array b64_encode_table
private static integer b64_encode_index = 0
private static hashtable b64_decode_table
readonly static integer array word_buf
readonly static integer word_buf_p = 0
readonly integer wbo // word_buf offset
readonly integer wp // the position of the current word starting from wbo, each word is 4 bytes (32 bits)
readonly integer bp // the position of the current bit in the current word, bits are written/read from left to right
readonly integer bits_written
readonly integer bits_read
readonly integer size_in_bits
readonly integer size_in_words // 1 word = 32 bits
method clear takes nothing returns nothing
local integer i = this.wbo
local integer j = this.wbo + this.size_in_words
loop
exitwhen i >= j
set word_buf[i] = 0
set i = i + 1
endloop
endmethod
static method create takes integer p_size_in_bits returns thistype
local thistype this
local integer i
local integer j
local integer l_size_in_words
static if DEBUG_MODE then
if p_size_in_bits <= 0 then
call panic("size_in_bits must be > 0")
endif
endif
set l_size_in_words = size_in_bits_to_size_in_words(p_size_in_bits)
if heads[l_size_in_words] != 0 then
set this = heads[l_size_in_words]
set heads[l_size_in_words] = heads[l_size_in_words].next
else
static if DEBUG_MODE then
if bit_buf_alloc + 1 > 8190 or word_buf_p + l_size_in_words > 8190 then
call panic("could not create a new BitBuf")
endif
endif
set bit_buf_alloc = bit_buf_alloc + 1
set this = bit_buf_alloc
set this.wbo = word_buf_p
set word_buf_p = word_buf_p + l_size_in_words
endif
set this.wp = 0
set this.bp = 0
set this.bits_written = 0
set this.bits_read = 0
set this.size_in_bits = p_size_in_bits
set this.size_in_words = l_size_in_words
call this.clear()
return this
endmethod
method destroy takes nothing returns nothing
set this.next = heads[this.size_in_words]
set heads[this.size_in_words] = this
endmethod
private method cache_state takes nothing returns nothing
local thistype cache = thistype(0)
set cache.wp = this.wp
set cache.bp = this.bp
set cache.bits_written = this.bits_written
set cache.bits_read = this.bits_read
endmethod
private method restore_state_from_cache takes nothing returns nothing
local thistype cache = thistype(0)
set this.wp = cache.wp
set this.bp = cache.bp
set this.bits_written = cache.bits_written
set this.bits_read = cache.bits_read
endmethod
method begin_writing takes nothing returns nothing
set this.wp = 0
set this.bp = 0
set this.bits_written = 0
call this.clear()
endmethod
method write_bits takes integer bits, integer bits_count returns nothing
local integer index
local integer v
local integer g1_bits_count
local integer g2_bits_count
local integer g1_bits
local integer g2_bits
set this.bits_written = this.bits_written + bits_count
static if DEBUG_MODE then
if this.bits_written > this.size_in_bits then
call panic("attempting to write " + I2S(this.bits_written) + " bits but BitBuf(" + I2S(this) + ").size_in_bits is " + I2S(this.size_in_bits))
endif
endif
if this.bp + bits_count > 32 then
// we can't write all the bits to the current word
// but we can split them into two groups, the 1st
// will have the bits that can be written to the current word
// and the 2nd will have the rest of the bits
//
set g1_bits_count = 32 - this.bp
set g2_bits_count = bits_count - g1_bits_count
set g1_bits = SHR(bits, g2_bits_count)
set index = this.wbo + this.wp
set v = word_buf[index]
set v = v + SHL(g1_bits, 32 - g1_bits_count - this.bp)
set word_buf[index] = v
set this.wp = this.wp + 1
set this.bp = 0
set g2_bits = SHL(bits, 32 - g2_bits_count)
set g2_bits = SHR(g2_bits, 32 - g2_bits_count)
set index = this.wbo + this.wp
set v = word_buf[index]
set v = v + SHL(g2_bits, 32 - g2_bits_count - this.bp)
set word_buf[index] = v
set this.bp = this.bp + g2_bits_count
else
set index = this.wbo + this.wp
set v = word_buf[index]
set v = v + SHL(bits, 32 - bits_count - this.bp)
set word_buf[index] = v
set this.bp = this.bp + bits_count
endif
endmethod
method write_u1 takes integer u1 returns nothing
debug if not (0x0 <= u1 and u1 <= 0x1) then
debug call panic("write_u1 range is 0 .. 1; value is " + I2S(u1))
debug endif
call this.write_bits(u1, 1)
endmethod
method write_u2 takes integer u2 returns nothing
debug if not (0x0 <= u2 and u2 <= 0x3) then
debug call panic("write_u2 range is 0 .. 3; value is " + I2S(u2))
debug endif
call this.write_bits(u2, 2)
endmethod
method write_u3 takes integer u3 returns nothing
debug if not (0x0 <= u3 and u3 <= 0x7) then
debug call panic("write_u3 range is 0 .. 7; value is " + I2S(u3))
debug endif
call this.write_bits(u3, 3)
endmethod
method write_u4 takes integer u4 returns nothing
debug if not (0x0 <= u4 and u4 <= 0xF) then
debug call panic("write_u4 range is 0 .. 15; value is " + I2S(u4))
debug endif
call this.write_bits(u4, 4)
endmethod
method write_u5 takes integer u5 returns nothing
debug if not (0x0 <= u5 and u5 <= 0x1F) then
debug call panic("write_u5 range is 0 .. 31; value is " + I2S(u5))
debug endif
call this.write_bits(u5, 5)
endmethod
method write_u6 takes integer u6 returns nothing
debug if not (0x0 <= u6 and u6 <= 0x3F) then
debug call panic("write_u6 range is 0 .. 63; value is " + I2S(u6))
debug endif
call this.write_bits(u6, 6)
endmethod
method write_u7 takes integer u7 returns nothing
debug if not (0x0 <= u7 and u7 <= 0x7F) then
debug call panic("write_u7 range is 0 .. 127; value is " + I2S(u7))
debug endif
call this.write_bits(u7, 7)
endmethod
method write_u8 takes integer u8 returns nothing
debug if not (0x0 <= u8 and u8 <= 0xFF) then
debug call panic("write_u8 range is 0 .. 255; value is " + I2S(u8))
debug endif
call this.write_bits(u8, 8)
endmethod
method write_u9 takes integer u9 returns nothing
debug if not (0x0 <= u9 and u9 <= 0x1FF) then
debug call panic("write_u9 range is 0 .. 511; value is " + I2S(u9))
debug endif
call this.write_bits(u9, 9)
endmethod
method write_u10 takes integer u10 returns nothing
debug if not (0x0 <= u10 and u10 <= 0x3FF) then
debug call panic("write_u10 range is 0 .. 1023; value is " + I2S(u10))
debug endif
call this.write_bits(u10, 10)
endmethod
method write_u11 takes integer u11 returns nothing
debug if not (0x0 <= u11 and u11 <= 0x7FF) then
debug call panic("write_u11 range is 0 .. 2047; value is " + I2S(u11))
debug endif
call this.write_bits(u11, 11)
endmethod
method write_u12 takes integer u12 returns nothing
debug if not (0x0 <= u12 and u12 <= 0xFFF) then
debug call panic("write_u12 range is 0 .. 4095; value is " + I2S(u12))
debug endif
call this.write_bits(u12, 12)
endmethod
method write_u13 takes integer u13 returns nothing
debug if not (0x0 <= u13 and u13 <= 0x1FFF) then
debug call panic("write_u13 range is 0 .. 8191; value is " + I2S(u13))
debug endif
call this.write_bits(u13, 13)
endmethod
method write_u14 takes integer u14 returns nothing
debug if not (0x0 <= u14 and u14 <= 0x3FFF) then
debug call panic("write_u14 range is 0 .. 16383; value is " + I2S(u14))
debug endif
call this.write_bits(u14, 14)
endmethod
method write_u15 takes integer u15 returns nothing
debug if not (0x0 <= u15 and u15 <= 0x7FFF) then
debug call panic("write_u15 range is 0 .. 32767; value is " + I2S(u15))
debug endif
call this.write_bits(u15, 15)
endmethod
method write_u16 takes integer u16 returns nothing
debug if not (0x0 <= u16 and u16 <= 0xFFFF) then
debug call panic("write_u16 range is 0 .. 65535; value is " + I2S(u16))
debug endif
call this.write_bits(u16, 16)
endmethod
method write_u17 takes integer u17 returns nothing
debug if not (0x0 <= u17 and u17 <= 0x1FFFF) then
debug call panic("write_u17 range is 0 .. 131071; value is " + I2S(u17))
debug endif
call this.write_bits(u17, 17)
endmethod
method write_u18 takes integer u18 returns nothing
debug if not (0x0 <= u18 and u18 <= 0x3FFFF) then
debug call panic("write_u18 range is 0 .. 262143; value is " + I2S(u18))
debug endif
call this.write_bits(u18, 18)
endmethod
method write_u19 takes integer u19 returns nothing
debug if not (0x0 <= u19 and u19 <= 0x7FFFF) then
debug call panic("write_u19 range is 0 .. 524287; value is " + I2S(u19))
debug endif
call this.write_bits(u19, 19)
endmethod
method write_u20 takes integer u20 returns nothing
debug if not (0x0 <= u20 and u20 <= 0xFFFFF) then
debug call panic("write_u20 range is 0 .. 1048575; value is " + I2S(u20))
debug endif
call this.write_bits(u20, 20)
endmethod
method write_u21 takes integer u21 returns nothing
debug if not (0x0 <= u21 and u21 <= 0x1FFFFF) then
debug call panic("write_u21 range is 0 .. 2097151; value is " + I2S(u21))
debug endif
call this.write_bits(u21, 21)
endmethod
method write_u22 takes integer u22 returns nothing
debug if not (0x0 <= u22 and u22 <= 0x3FFFFF) then
debug call panic("write_u22 range is 0 .. 4194303; value is " + I2S(u22))
debug endif
call this.write_bits(u22, 22)
endmethod
method write_u23 takes integer u23 returns nothing
debug if not (0x0 <= u23 and u23 <= 0x7FFFFF) then
debug call panic("write_u23 range is 0 .. 8388607; value is " + I2S(u23))
debug endif
call this.write_bits(u23, 23)
endmethod
method write_u24 takes integer u24 returns nothing
debug if not (0x0 <= u24 and u24 <= 0xFFFFFF) then
debug call panic("write_u24 range is 0 .. 16777215; value is " + I2S(u24))
debug endif
call this.write_bits(u24, 24)
endmethod
method write_u25 takes integer u25 returns nothing
debug if not (0x0 <= u25 and u25 <= 0x1FFFFFF) then
debug call panic("write_u25 range is 0 .. 33554431; value is " + I2S(u25))
debug endif
call this.write_bits(u25, 25)
endmethod
method write_u26 takes integer u26 returns nothing
debug if not (0x0 <= u26 and u26 <= 0x3FFFFFF) then
debug call panic("write_u26 range is 0 .. 67108863; value is " + I2S(u26))
debug endif
call this.write_bits(u26, 26)
endmethod
method write_u27 takes integer u27 returns nothing
debug if not (0x0 <= u27 and u27 <= 0x7FFFFFF) then
debug call panic("write_u27 range is 0 .. 134217727; value is " + I2S(u27))
debug endif
call this.write_bits(u27, 27)
endmethod
method write_u28 takes integer u28 returns nothing
debug if not (0x0 <= u28 and u28 <= 0xFFFFFFF) then
debug call panic("write_u28 range is 0 .. 268435455; value is " + I2S(u28))
debug endif
call this.write_bits(u28, 28)
endmethod
method write_u29 takes integer u29 returns nothing
debug if not (0x0 <= u29 and u29 <= 0x1FFFFFFF) then
debug call panic("write_u29 range is 0 .. 536870911; value is " + I2S(u29))
debug endif
call this.write_bits(u29, 29)
endmethod
method write_u30 takes integer u30 returns nothing
debug if not (0x0 <= u30 and u30 <= 0x3FFFFFFF) then
debug call panic("write_u30 range is 0 .. 1073741823; value is " + I2S(u30))
debug endif
call this.write_bits(u30, 30)
endmethod
method write_u31 takes integer u31 returns nothing
debug if not (0x0 <= u31 and u31 <= 0x7FFFFFFF) then
debug call panic("write_u31 range is 0 .. 2147483647; value is " + I2S(u31))
debug endif
call this.write_bits(u31, 31)
endmethod
method write_signed_bits takes integer bits, integer bits_count returns nothing
if bits < 0 then
call this.write_bits(0x1, 1)
set bits = SHL(bits, 33 - bits_count)
set bits = SHR(bits, 33 - bits_count)
else
call this.write_bits(0x0, 1)
endif
call this.write_bits(bits, bits_count - 1)
endmethod
method write_s2 takes integer s2 returns nothing
debug if not (0xFFFFFFFE <= s2 and s2 <= 0x1) then
debug call panic("write_s2 range is -2 .. 1; value is " + I2S(s2))
debug endif
call this.write_signed_bits(s2, 2)
endmethod
method write_s3 takes integer s3 returns nothing
debug if not (0xFFFFFFFC <= s3 and s3 <= 0x3) then
debug call panic("write_s3 range is -4 .. 3; value is " + I2S(s3))
debug endif
call this.write_signed_bits(s3, 3)
endmethod
method write_s4 takes integer s4 returns nothing
debug if not (0xFFFFFFF8 <= s4 and s4 <= 0x7) then
debug call panic("write_s4 range is -8 .. 7; value is " + I2S(s4))
debug endif
call this.write_signed_bits(s4, 4)
endmethod
method write_s5 takes integer s5 returns nothing
debug if not (0xFFFFFFF0 <= s5 and s5 <= 0xF) then
debug call panic("write_s5 range is -16 .. 15; value is " + I2S(s5))
debug endif
call this.write_signed_bits(s5, 5)
endmethod
method write_s6 takes integer s6 returns nothing
debug if not (0xFFFFFFE0 <= s6 and s6 <= 0x1F) then
debug call panic("write_s6 range is -32 .. 31; value is " + I2S(s6))
debug endif
call this.write_signed_bits(s6, 6)
endmethod
method write_s7 takes integer s7 returns nothing
debug if not (0xFFFFFFC0 <= s7 and s7 <= 0x3F) then
debug call panic("write_s7 range is -64 .. 63; value is " + I2S(s7))
debug endif
call this.write_signed_bits(s7, 7)
endmethod
method write_s8 takes integer s8 returns nothing
debug if not (0xFFFFFF80 <= s8 and s8 <= 0x7F) then
debug call panic("write_s8 range is -128 .. 127; value is " + I2S(s8))
debug endif
call this.write_signed_bits(s8, 8)
endmethod
method write_s9 takes integer s9 returns nothing
debug if not (0xFFFFFF00 <= s9 and s9 <= 0xFF) then
debug call panic("write_s9 range is -256 .. 255; value is " + I2S(s9))
debug endif
call this.write_signed_bits(s9, 9)
endmethod
method write_s10 takes integer s10 returns nothing
debug if not (0xFFFFFE00 <= s10 and s10 <= 0x1FF) then
debug call panic("write_s10 range is -512 .. 511; value is " + I2S(s10))
debug endif
call this.write_signed_bits(s10, 10)
endmethod
method write_s11 takes integer s11 returns nothing
debug if not (0xFFFFFC00 <= s11 and s11 <= 0x3FF) then
debug call panic("write_s11 range is -1024 .. 1023; value is " + I2S(s11))
debug endif
call this.write_signed_bits(s11, 11)
endmethod
method write_s12 takes integer s12 returns nothing
debug if not (0xFFFFF800 <= s12 and s12 <= 0x7FF) then
debug call panic("write_s12 range is -2048 .. 2047; value is " + I2S(s12))
debug endif
call this.write_signed_bits(s12, 12)
endmethod
method write_s13 takes integer s13 returns nothing
debug if not (0xFFFFF000 <= s13 and s13 <= 0xFFF) then
debug call panic("write_s13 range is -4096 .. 4095; value is " + I2S(s13))
debug endif
call this.write_signed_bits(s13, 13)
endmethod
method write_s14 takes integer s14 returns nothing
debug if not (0xFFFFE000 <= s14 and s14 <= 0x1FFF) then
debug call panic("write_s14 range is -8192 .. 8191; value is " + I2S(s14))
debug endif
call this.write_signed_bits(s14, 14)
endmethod
method write_s15 takes integer s15 returns nothing
debug if not (0xFFFFC000 <= s15 and s15 <= 0x3FFF) then
debug call panic("write_s15 range is -16384 .. 16383; value is " + I2S(s15))
debug endif
call this.write_signed_bits(s15, 15)
endmethod
method write_s16 takes integer s16 returns nothing
debug if not (0xFFFF8000 <= s16 and s16 <= 0x7FFF) then
debug call panic("write_s16 range is -32768 .. 32767; value is " + I2S(s16))
debug endif
call this.write_signed_bits(s16, 16)
endmethod
method write_s17 takes integer s17 returns nothing
debug if not (0xFFFF0000 <= s17 and s17 <= 0xFFFF) then
debug call panic("write_s17 range is -65536 .. 65535; value is " + I2S(s17))
debug endif
call this.write_signed_bits(s17, 17)
endmethod
method write_s18 takes integer s18 returns nothing
debug if not (0xFFFE0000 <= s18 and s18 <= 0x1FFFF) then
debug call panic("write_s18 range is -131072 .. 131071; value is " + I2S(s18))
debug endif
call this.write_signed_bits(s18, 18)
endmethod
method write_s19 takes integer s19 returns nothing
debug if not (0xFFFC0000 <= s19 and s19 <= 0x3FFFF) then
debug call panic("write_s19 range is -262144 .. 262143; value is " + I2S(s19))
debug endif
call this.write_signed_bits(s19, 19)
endmethod
method write_s20 takes integer s20 returns nothing
debug if not (0xFFF80000 <= s20 and s20 <= 0x7FFFF) then
debug call panic("write_s20 range is -524288 .. 524287; value is " + I2S(s20))
debug endif
call this.write_signed_bits(s20, 20)
endmethod
method write_s21 takes integer s21 returns nothing
debug if not (0xFFF00000 <= s21 and s21 <= 0xFFFFF) then
debug call panic("write_s21 range is -1048576 .. 1048575; value is " + I2S(s21))
debug endif
call this.write_signed_bits(s21, 21)
endmethod
method write_s22 takes integer s22 returns nothing
debug if not (0xFFE00000 <= s22 and s22 <= 0x1FFFFF) then
debug call panic("write_s22 range is -2097152 .. 2097151; value is " + I2S(s22))
debug endif
call this.write_signed_bits(s22, 22)
endmethod
method write_s23 takes integer s23 returns nothing
debug if not (0xFFC00000 <= s23 and s23 <= 0x3FFFFF) then
debug call panic("write_s23 range is -4194304 .. 4194303; value is " + I2S(s23))
debug endif
call this.write_signed_bits(s23, 23)
endmethod
method write_s24 takes integer s24 returns nothing
debug if not (0xFF800000 <= s24 and s24 <= 0x7FFFFF) then
debug call panic("write_s24 range is -8388608 .. 8388607; value is " + I2S(s24))
debug endif
call this.write_signed_bits(s24, 24)
endmethod
method write_s25 takes integer s25 returns nothing
debug if not (0xFF000000 <= s25 and s25 <= 0xFFFFFF) then
debug call panic("write_s25 range is -16777216 .. 16777215; value is " + I2S(s25))
debug endif
call this.write_signed_bits(s25, 25)
endmethod
method write_s26 takes integer s26 returns nothing
debug if not (0xFE000000 <= s26 and s26 <= 0x1FFFFFF) then
debug call panic("write_s26 range is -33554432 .. 33554431; value is " + I2S(s26))
debug endif
call this.write_signed_bits(s26, 26)
endmethod
method write_s27 takes integer s27 returns nothing
debug if not (0xFC000000 <= s27 and s27 <= 0x3FFFFFF) then
debug call panic("write_s27 range is -67108864 .. 67108863; value is " + I2S(s27))
debug endif
call this.write_signed_bits(s27, 27)
endmethod
method write_s28 takes integer s28 returns nothing
debug if not (0xF8000000 <= s28 and s28 <= 0x7FFFFFF) then
debug call panic("write_s28 range is -134217728 .. 134217727; value is " + I2S(s28))
debug endif
call this.write_signed_bits(s28, 28)
endmethod
method write_s29 takes integer s29 returns nothing
debug if not (0xF0000000 <= s29 and s29 <= 0xFFFFFFF) then
debug call panic("write_s29 range is -268435456 .. 268435455; value is " + I2S(s29))
debug endif
call this.write_signed_bits(s29, 29)
endmethod
method write_s30 takes integer s30 returns nothing
debug if not (0xE0000000 <= s30 and s30 <= 0x1FFFFFFF) then
debug call panic("write_s30 range is -536870912 .. 536870911; value is " + I2S(s30))
debug endif
call this.write_signed_bits(s30, 30)
endmethod
method write_s31 takes integer s31 returns nothing
debug if not (0xC0000000 <= s31 and s31 <= 0x3FFFFFFF) then
debug call panic("write_s31 range is -1073741824 .. 1073741823; value is " + I2S(s31))
debug endif
call this.write_signed_bits(s31, 31)
endmethod
method write_s32 takes integer s32 returns nothing
// debug if not (0x80000000 <= s32 and s32 <= 0x7FFFFFFF) then
// debug call panic("write_s32 range is -2147483648 .. 2147483647; value is " + I2S(s32))
// debug endif
call this.write_signed_bits(s32, 32)
endmethod
static if ENABLE_REAL_READ_WRITE then
method write_r16 takes real r32 returns nothing
local integer u32 = real_as_int(r32)
local integer u16 = SHR(u32, 16) // chop the lower 16 bits of the mantisa
this.write_u16(u16)
endmethod
methd write_r32 takes real r32 returns nothing
local integer s32 = real_as_int(r32)
call this.write_s32(s32)
endmethod
endif
method write_bool takes boolean b returns nothing
if b then
call this.write_u1(0x1)
else
call this.write_u1(0x0)
endif
endmethod
method begin_reading takes nothing returns nothing
set this.wp = 0
set this.bp = 0
set this.bits_read = 0
endmethod
method read_bits takes integer bits_count returns integer
local integer index
local integer v
local integer g1_bits_count
local integer g2_bits_count
local integer g1_bits
local integer g2_bits
local integer result_bits = 0
set this.bits_read = this.bits_read + bits_count
static if DEBUG_MODE then
if this.bits_read > this.size_in_bits then
call panic("attempting to read " + I2S(this.bits_read) + " bits but BitBuf(" + I2S(this) + ").size_in_bits is " + I2S(this.size_in_bits))
endif
endif
if this.bp + bits_count > 32 then
// we can't read all the bits from the current word
// but we can split them into two groups, the 1st
// will have the bits that can be read from the current word
// and the 2nd will have the rest of the bits
//
set g1_bits_count = 32 - this.bp
set g2_bits_count = bits_count - g1_bits_count
set index = this.wbo + this.wp
set v = word_buf[index]
set g1_bits = SHL(v, this.bp)
set g1_bits = SHR(g1_bits, this.bp)
set result_bits = result_bits + SHL(g1_bits, g2_bits_count)
set this.wp = this.wp + 1
set this.bp = 0
set index = this.wbo + this.wp
set v = word_buf[index]
set g2_bits = SHR(v, 32 - g2_bits_count)
set result_bits = result_bits + g2_bits
set this.bp = this.bp + g2_bits_count
else
set index = this.wbo + this.wp
set v = word_buf[index]
set result_bits = SHL(v, this.bp)
set result_bits = SHR(result_bits, 32 - bits_count)
set this.bp = this.bp + bits_count
if this.bp == 32 then
set this.wp = this.wp + 1
set this.bp = 0
endif
endif
return result_bits
endmethod
method read_u1 takes nothing returns integer
return this.read_bits(1)
endmethod
method read_u2 takes nothing returns integer
return this.read_bits(2)
endmethod
method read_u3 takes nothing returns integer
return this.read_bits(3)
endmethod
method read_u4 takes nothing returns integer
return this.read_bits(4)
endmethod
method read_u5 takes nothing returns integer
return this.read_bits(5)
endmethod
method read_u6 takes nothing returns integer
return this.read_bits(6)
endmethod
method read_u7 takes nothing returns integer
return this.read_bits(7)
endmethod
method read_u8 takes nothing returns integer
return this.read_bits(8)
endmethod
method read_u9 takes nothing returns integer
return this.read_bits(9)
endmethod
method read_u10 takes nothing returns integer
return this.read_bits(10)
endmethod
method read_u11 takes nothing returns integer
return this.read_bits(11)
endmethod
method read_u12 takes nothing returns integer
return this.read_bits(12)
endmethod
method read_u13 takes nothing returns integer
return this.read_bits(13)
endmethod
method read_u14 takes nothing returns integer
return this.read_bits(14)
endmethod
method read_u15 takes nothing returns integer
return this.read_bits(15)
endmethod
method read_u16 takes nothing returns integer
return this.read_bits(16)
endmethod
method read_u17 takes nothing returns integer
return this.read_bits(17)
endmethod
method read_u18 takes nothing returns integer
return this.read_bits(18)
endmethod
method read_u19 takes nothing returns integer
return this.read_bits(19)
endmethod
method read_u20 takes nothing returns integer
return this.read_bits(20)
endmethod
method read_u21 takes nothing returns integer
return this.read_bits(21)
endmethod
method read_u22 takes nothing returns integer
return this.read_bits(22)
endmethod
method read_u23 takes nothing returns integer
return this.read_bits(23)
endmethod
method read_u24 takes nothing returns integer
return this.read_bits(24)
endmethod
method read_u25 takes nothing returns integer
return this.read_bits(25)
endmethod
method read_u26 takes nothing returns integer
return this.read_bits(26)
endmethod
method read_u27 takes nothing returns integer
return this.read_bits(27)
endmethod
method read_u28 takes nothing returns integer
return this.read_bits(28)
endmethod
method read_u29 takes nothing returns integer
return this.read_bits(29)
endmethod
method read_u30 takes nothing returns integer
return this.read_bits(30)
endmethod
method read_u31 takes nothing returns integer
return this.read_bits(31)
endmethod
method read_s2 takes nothing returns integer
return SHL(this.read_bits(2), 30) / pow2(30)
endmethod
method read_s3 takes nothing returns integer
return SHL(this.read_bits(3), 29) / pow2(29)
endmethod
method read_s4 takes nothing returns integer
return SHL(this.read_bits(4), 28) / pow2(28)
endmethod
method read_s5 takes nothing returns integer
return SHL(this.read_bits(5), 27) / pow2(27)
endmethod
method read_s6 takes nothing returns integer
return SHL(this.read_bits(6), 26) / pow2(26)
endmethod
method read_s7 takes nothing returns integer
return SHL(this.read_bits(7), 25) / pow2(25)
endmethod
method read_s8 takes nothing returns integer
return SHL(this.read_bits(8), 24) / pow2(24)
endmethod
method read_s9 takes nothing returns integer
return SHL(this.read_bits(9), 23) / pow2(23)
endmethod
method read_s10 takes nothing returns integer
return SHL(this.read_bits(10), 22) / pow2(22)
endmethod
method read_s11 takes nothing returns integer
return SHL(this.read_bits(11), 21) / pow2(21)
endmethod
method read_s12 takes nothing returns integer
return SHL(this.read_bits(12), 20) / pow2(20)
endmethod
method read_s13 takes nothing returns integer
return SHL(this.read_bits(13), 19) / pow2(19)
endmethod
method read_s14 takes nothing returns integer
return SHL(this.read_bits(14), 18) / pow2(18)
endmethod
method read_s15 takes nothing returns integer
return SHL(this.read_bits(15), 17) / pow2(17)
endmethod
method read_s16 takes nothing returns integer
return SHL(this.read_bits(16), 16) / pow2(16)
endmethod
method read_s17 takes nothing returns integer
return SHL(this.read_bits(17), 15) / pow2(15)
endmethod
method read_s18 takes nothing returns integer
return SHL(this.read_bits(18), 14) / pow2(14)
endmethod
method read_s19 takes nothing returns integer
return SHL(this.read_bits(19), 13) / pow2(13)
endmethod
method read_s20 takes nothing returns integer
return SHL(this.read_bits(20), 12) / pow2(12)
endmethod
method read_s21 takes nothing returns integer
return SHL(this.read_bits(21), 11) / pow2(11)
endmethod
method read_s22 takes nothing returns integer
return SHL(this.read_bits(22), 10) / pow2(10)
endmethod
method read_s23 takes nothing returns integer
return SHL(this.read_bits(23), 9) / pow2(9)
endmethod
method read_s24 takes nothing returns integer
return SHL(this.read_bits(24), 8) / pow2(8)
endmethod
method read_s25 takes nothing returns integer
return SHL(this.read_bits(25), 7) / pow2(7)
endmethod
method read_s26 takes nothing returns integer
return SHL(this.read_bits(26), 6) / pow2(6)
endmethod
method read_s27 takes nothing returns integer
return SHL(this.read_bits(27), 5) / pow2(5)
endmethod
method read_s28 takes nothing returns integer
return SHL(this.read_bits(28), 4) / pow2(4)
endmethod
method read_s29 takes nothing returns integer
return SHL(this.read_bits(29), 3) / pow2(3)
endmethod
method read_s30 takes nothing returns integer
return SHL(this.read_bits(30), 2) / pow2(2)
endmethod
method read_s31 takes nothing returns integer
return SHL(this.read_bits(31), 1) / pow2(1)
endmethod
method read_s32 takes nothing returns integer
// return SHL(this.read_bits(32), 0) / pow2(0)
return this.read_bits(32)
endmethod
static if ENABLE_REAL_READ_WRITE then
method read_r16 takes nothing returns real
return int_as_real(SHL(this.read_u16(), 16))
endmethod
method read_r32 takes nothing returns real
return int_as_real(this.read_s32())
endmethod
endif
method read_bool takes nothing returns boolean
return this.read_u1() == 0x1
endmethod
private static method b64_encode_table_init_8 takes string p1, string p2, string p3, string p4, string p5, string p6, string p7, string p8 returns nothing
set b64_encode_table[b64_encode_index + 0] = p1
set b64_encode_table[b64_encode_index + 1] = p2
set b64_encode_table[b64_encode_index + 2] = p3
set b64_encode_table[b64_encode_index + 3] = p4
set b64_encode_table[b64_encode_index + 4] = p5
set b64_encode_table[b64_encode_index + 5] = p6
set b64_encode_table[b64_encode_index + 6] = p7
set b64_encode_table[b64_encode_index + 7] = p8
set b64_encode_index = b64_encode_index + 8
endmethod
private static method b64_encode_table_init takes nothing returns nothing
call b64_encode_table_init_8("A", "B", "C", "D", "E", "F", "G", "H")
call b64_encode_table_init_8("I", "J", "K", "L", "M", "N", "O", "P")
call b64_encode_table_init_8("Q", "R", "S", "T", "U", "V", "W", "X")
call b64_encode_table_init_8("Y", "Z", "a", "b", "c", "d", "e", "f")
call b64_encode_table_init_8("g", "h", "i", "j", "k", "l", "m", "n")
call b64_encode_table_init_8("o", "p", "q", "r", "s", "t", "u", "v")
call b64_encode_table_init_8("w", "x", "y", "z", "0", "1", "2", "3")
call b64_encode_table_init_8("4", "5", "6", "7", "8", "9", "+", "/")
endmethod
static method b64_from_int takes integer i returns string
return b64_encode_table[i]
endmethod
private static method b64_decode_table_init takes nothing returns nothing
local integer i
set b64_decode_table = InitHashtable()
set i = 0
loop
exitwhen i > 63
// StringHash is case-insensitive, so there's no point in trying to store the lowercase letters
if i <= 25 or i >= 52 then
call SaveInteger(b64_decode_table, 0, StringHash(b64_from_int(i)), i)
endif
set i = i + 1
endloop
endmethod
static method b64_to_int takes string b64 returns integer
local integer i = LoadInteger(b64_decode_table, 0, StringHash(b64))
if i <= 25 and b64 == StringCase(b64, /*lowercase*/false) then
set i = i + 26
endif
return i
endmethod
private static method onInit takes nothing returns nothing
call b64_encode_table_init()
call b64_decode_table_init()
endmethod
method to_base64 takes nothing returns string
local integer result_digits_count = round_up_nearest(this.bits_written, 6) / 6
local string result = ""
local integer i
local integer u6
local string b64
call this.cache_state()
call this.begin_reading()
set i = 1
loop
exitwhen i > result_digits_count
set u6 = this.read_u6()
set b64 = b64_from_int(u6)
set result = result + b64
set i = i + 1
endloop
call this.restore_state_from_cache()
return result
endmethod
static method base64_apply_colors takes string base64, string uc, string lc, string d, string p returns string
local string result = ""
local integer i
local string c
local integer o
set i = 0
loop
set c = SubString(base64, i, i + 1)
exitwhen c == ""
set o = b64_to_int(c)
set result = result + "|cff"
if o <= 25 then
set result = result + uc
elseif o <= 51 then
set result = result + lc
elseif o <= 61 then
set result = result + d
else // if o <= 63 then
set result = result + p
endif
set result = result + c + "|r"
set i = i + 1
endloop
return result
endmethod
static method from_base64 takes string base64 returns thistype
local thistype this = create(StringLength(base64) * 6)
local integer i
local string c
local integer o
set i = 0
loop
set c = SubString(base64, i, i + 1)
exitwhen c == ""
set o = b64_to_int(c)
call this.write_u6(o)
set i = i + 1
endloop
call this.begin_reading()
return this
endmethod
// reference: https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm
//
static method luhn_generate_check_digit takes string base64 returns string
local integer sum
local integer factor
local integer i
local integer curr_digit
local integer result_ordinal
local string result
set sum = 0
set factor = 2
set i = StringLength(base64) - 1
loop
exitwhen i < 0
set curr_digit = b64_to_int(SubString(base64, i, i + 1))
set curr_digit = curr_digit * factor
if curr_digit >= 64 then
set curr_digit = curr_digit - 63 // 63 = 64 - 1 (base = 64)
endif
set sum = sum + curr_digit
if factor == 2 then
set factor = 1
else
set factor = 2
endif
set i = i - 1
endloop
set result_ordinal = round_up_nearest(sum, 64) - sum
set result = b64_from_int(result_ordinal)
return result
endmethod
static method luhn_is_valid takes string base64 returns boolean
local integer sum
local integer factor
local integer i
local integer curr_digit
set sum = 0
set factor = 1
set i = StringLength(base64) - 1
loop
exitwhen i < 0
set curr_digit = b64_to_int(SubString(base64, i, i + 1))
set curr_digit = curr_digit * factor
if curr_digit >= 64 then
set curr_digit = curr_digit - 63
endif
set sum = sum + curr_digit
if factor == 1 then
set factor = 2
else
set factor = 1
endif
set i = i - 1
endloop
return (sum - sum / 64 * 64) == 0
endmethod
endstruct
endlibrary
Edit: the destroy method (which did nothing previously) is now disabled (panics when called) because deallocating the memory used by a
BitBuf
is kind of hard. =)Edit2: the destroy method should work correctly now (apparently it wasn't that hard after all =))
Attachments
Last edited: