; ------------------------------------------------------------------------
; HeavyThing x86_64 assembly language library and showcase programs
; Copyright © 2015-2018 2 Ton Digital
; Homepage: https://2ton.com.au/
; Author: Jeff Marrison <jeff@2ton.com.au>
;
; This file is part of the HeavyThing library.
;
; HeavyThing is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License, or
; (at your option) any later version.
;
; HeavyThing 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 the HeavyThing library. If not, see <http://www.gnu.org/licenses/>.
; ------------------------------------------------------------------------
;
; buffer.inc: byte buffer goods with some extra conveniences
;
; every buffer object contains a few goodies:
buffer_endptr_ofs = 0
buffer_length_ofs = 8
buffer_itself_ofs = 16
buffer_size_ofs = 24
buffer_user_ofs = 32 ; 24 bytes here that we don't use
buffer_object_size = 56
buffer_default_size = 256 ; how large is the actual initial buffer by default?
if used buffer$new | defined include_everything
; no arguments, returns new initialized buffer in rax
falign
buffer$new:
prolog buffer$new
mov rdi, buffer_object_size
call heap$alloc
push rax
mov rdi, buffer_default_size
call heap$alloc
mov rdi, rax
pop rax
xor edx, edx
mov [rax+buffer_endptr_ofs], rdi
mov [rax+buffer_length_ofs], rdx
mov [rax+buffer_itself_ofs], rdi
mov qword [rax+buffer_size_ofs], buffer_default_size
; leave the user 16 bytes alone
; return in rax is good
epilog
end if
if used buffer$destroy | defined include_everything
; single arg in rdi: buffer object
falign
buffer$destroy:
prolog buffer$destroy
push rdi
mov rdi, [rdi+buffer_itself_ofs]
call heap$free
pop rdi
call heap$free
epilog
end if
if used buffer$copy | defined include_everything
; single arg in rdi: makes a clone of this buffer and returns the new one in rax
falign
buffer$copy:
prolog buffer$copy
sub rsp, 16
mov [rsp], rdi
mov rdi, [rdi+buffer_size_ofs]
call heap$alloc
mov [rsp+8], rax
mov rdi, buffer_object_size
call heap$alloc
mov rsi, [rsp+8] ; the buffer itself
mov rdx, rsi
mov rdi, [rsp]
add rdx, qword [rdi+buffer_length_ofs]
mov [rax+buffer_endptr_ofs], rdx
mov rdx, qword [rdi+buffer_length_ofs]
mov [rax+buffer_length_ofs], rdx
mov [rax+buffer_itself_ofs], rsi
mov rdx, [rdi+buffer_size_ofs]
mov rcx, [rdi+buffer_user_ofs]
mov r8, [rdi+buffer_user_ofs+8]
mov [rax+buffer_size_ofs], rdx
mov [rax+buffer_user_ofs], rcx
mov [rax+buffer_user_ofs+8], r8
; now we need to preserve oru return in rax, then issue a memcpy
mov rdx, [rdi+buffer_length_ofs]
mov [rsp], rax
mov rsi, [rdi+buffer_itself_ofs]
mov rdi, [rax+buffer_itself_ofs]
call memcpy
mov rax, [rsp]
add rsp, 16
epilog
end if
if used buffer$reset_reserve | defined include_everything
; two arguments: rdi == buffer object, rsi == length to reserve/verify enough space for
falign
buffer$reset_reserve:
prolog buffer$reset_reserve
push rdi
call buffer$reserve
pop rdi
call buffer$reset
epilog
end if
if used buffer$reserve | defined include_everything
; two arguments: rdi == buffer object, rsi == length to reserve/verify enough space for
falign
buffer$reserve:
prolog buffer$reserve
mov r8, [rdi+buffer_size_ofs]
mov r9, [rdi+buffer_length_ofs]
mov rdx, r8
sub rdx, r9
shl r8, 1
cmp rdx, rsi
jb .needmore
epilog
calign
.needmore:
mov rdx, r8
sub rdx, r9
cmp rdx, rsi
jae .increase
; else, we need to make it bigger
shl r8, 1
jmp .needmore
calign
.increase:
; r8 has our new size, r9 has our length
; we want [rsp] to be rdi, and [rsp+8] to be [rdi+buffer_itself_ofs] , and [rsp+16] to be the new size
push r8
push qword [rdi+buffer_itself_ofs]
push rdi
mov rdi, r8
call heap$alloc
mov rdi, [rsp]
mov rdx, [rsp+16]
mov [rdi+buffer_size_ofs], rdx
mov r9, [rdi+buffer_length_ofs]
mov [rdi+buffer_itself_ofs], rax
add rax, r9
mov [rdi+buffer_endptr_ofs], rax
test r9, r9
jz .increase_nocopy
sub rax, r9
mov rdx, r9 ; length
mov rdi, rax ; destination buffer
mov rsi, [rsp+8] ; old buffer
call memcpy
mov rdi, [rsp+8] ; old buffer
call heap$free
add rsp, 24
epilog
calign
.increase_nocopy:
; length of old spot was zero, but we did increase the buffer
; so all we need to do is free the old one
mov rdi, [rsp+8]
call heap$free
add rsp, 24
epilog
end if
if used buffer$reset | defined include_everything
; single arg rdi: buffer object, sets length to 0 and resets endptr back to the beginning
falign
buffer$reset:
prolog buffer$reset
mov rsi, [rdi+buffer_itself_ofs]
mov qword [rdi+buffer_length_ofs], 0
mov [rdi+buffer_endptr_ofs], rsi
epilog
end if
if used buffer$truncate | defined include_everything
; two arguments: rdi: buffer object, rsi: number of bytes to remove from the end
falign
buffer$truncate:
prolog buffer$truncate
mov rcx, [rdi+buffer_length_ofs]
mov rdx, [rdi+buffer_endptr_ofs]
cmp rsi, rcx
jae .resetonly
sub rcx, rsi
sub rdx, rsi
mov [rdi+buffer_length_ofs], rcx
mov [rdi+buffer_endptr_ofs], rdx
epilog
calign
.resetonly:
mov rsi, [rdi+buffer_itself_ofs]
mov qword [rdi+buffer_length_ofs], 0
mov [rdi+buffer_endptr_ofs], rsi
epilog
end if
if used buffer$consume | defined include_everything
; two arguments: rdi: buffer object, rsi: number of bytes to remove from the head of the buffer
falign
buffer$consume:
prolog buffer$consume
test rsi, rsi
jz .nothingtodo
mov rcx, [rdi+buffer_length_ofs]
cmp rsi, rcx
jae .resetonly
mov r8, [rdi+buffer_itself_ofs]
mov rdx, rcx
mov r9, r8
sub rdx, rsi ; the new nonzero length, and also the size arg for our memmove
add rsi, r8 ; the source pointer for our memmove
add r9, rdx
mov [rdi+buffer_length_ofs], rdx
mov [rdi+buffer_endptr_ofs], r9
mov rdi, [rdi+buffer_itself_ofs] ; the destination
call memmove
epilog
calign
.resetonly:
mov rsi, [rdi+buffer_itself_ofs]
mov qword [rdi+buffer_length_ofs], 0
mov [rdi+buffer_endptr_ofs], rsi
.nothingtodo:
epilog
end if
if used buffer$append_nocopy | defined include_everything
; two arguments: rdi: buffer object, rsi: number of bytes to move endptr and length forward by
falign
buffer$append_nocopy:
prolog buffer$append_nocopy
add qword [rdi+buffer_length_ofs], rsi
add qword [rdi+buffer_endptr_ofs], rsi
epilog
end if
if used buffer$append | defined include_everything
; three arguments: rdi: buffer object, rsi: source byte buffer, rdx: source byte buffer length (void *, whatever)
falign
buffer$append:
prolog buffer$append
test rdx, rdx
jz .nothingtodo
; we want [rsp] to be rdi, [rsp+8] to be rsi, [rsp+16] to be rdx
push rdx rsi rdi
mov rsi, rdx
call buffer$reserve
pop rdi rsi rdx
mov rcx, rdi
mov rdi, [rdi+buffer_endptr_ofs]
add qword [rcx+buffer_endptr_ofs], rdx
add qword [rcx+buffer_length_ofs], rdx
call memcpy
epilog
calign
.nothingtodo:
epilog
end if
if used buffer$insert | defined include_everything
; four arguments: rdi == buffer object, rsi == offset, rdx == source byte buffer, rcx == source byte buffer length
falign
buffer$insert:
prolog buffer$insert
cmp rsi, [rdi+buffer_length_ofs]
jae .append
sub rsp, 32
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+16], rdx
mov [rsp+24], rcx
mov rsi, rcx
call buffer$reserve
; make sure the offset is valid
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rdx, [rdi+buffer_length_ofs]
cmp rsi, rdx
cmova rsi, rdx
mov [rsp+8], rsi
; move the contents, only if rsi != buffer_length_ofs
jae .nomove
sub rdx, rsi ; bytes remaining from offset to end of what was there, bytecount of move
; source of move is buffer[offset]
add rsi, [rdi+buffer_itself_ofs]
; destination of move is buffer[offset+sourcelength]
mov rdi, rsi
add rdi, [rsp+24]
call memmove
calign
.nomove:
mov rdi, [rsp]
mov rsi, [rsp+16]
mov rdx, [rsp+24]
mov rdi, [rdi+buffer_itself_ofs]
add rdi, [rsp+8]
call memcpy
; update our buffer length and endptr offset by the source length
mov rdi, [rsp]
mov rcx, [rsp+24]
add qword [rdi+buffer_length_ofs], rcx
add qword [rdi+buffer_endptr_ofs], rcx
add rsp, 32
epilog
calign
.append:
mov rsi, rdx
mov rdx, rcx
call buffer$append
epilog
end if
if used buffer$remove | defined include_everything
; three arguments: rdi == buffer object, rsi == offset, rdx == length to remove from it
falign
buffer$remove:
prolog buffer$remove
sub rsp, 24
mov rcx, [rdi+buffer_length_ofs]
mov rax, rsi
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+16], rdx
; if the offset is past the end, don't do anything
cmp rsi, rcx
jae .alldone
; make sure the amount to remove isn't out of bounds either
; if (offset+length to remove >= current length) truncate length to offset and be done
add rax, rdx
cmp rax, rcx
jae .truncate
; otherwise, our new length is our current length - rdx, our move destination is buffer[offset], our source is buffer[offset+lengthtoremove]
; and our move count is current length - (offset+length)
mov r8, rcx ; current length
mov r9, rsi ; offset
sub r8, rdx ; current length - lengthtoremove
add r9, rdx ; offset + lengthtoremove
mov [rdi+buffer_length_ofs], r8 ; new length is current length - length to remove
mov rsi, [rdi+buffer_itself_ofs]
mov r10, rsi
add r10, r8
mov [rdi+buffer_endptr_ofs], r10
sub rcx, r9 ; move count = current length - (offset + length to remove)
add rsi, r9 ; source of move = offset + length to remove
mov rdi, [rdi+buffer_itself_ofs]
add rdi, [rsp+8]
mov rdx, rcx
call memmove
add rsp, 24
epilog
calign
.alldone:
add rsp, 24
epilog
calign
.truncate:
mov rax, [rdi+buffer_itself_ofs]
mov [rdi+buffer_length_ofs], rsi
add rax, rsi
mov [rdi+buffer_endptr_ofs], rax
add rsp, 24
epilog
end if
if used buffer$append_byte | defined include_everything
; two arguments: rdi: buffer object, esi (sil): byte to append
falign
buffer$append_byte:
prolog buffer$append_byte
push rdi rsi
mov esi, 1
call buffer$reserve
pop rsi rdi
mov rdx, [rdi+buffer_endptr_ofs]
mov byte [rdx], sil
add qword [rdi+buffer_endptr_ofs], 1
add qword [rdi+buffer_length_ofs], 1
epilog
end if
if used buffer$append_byte_noreserve | defined include_everything
; same as the above, but we do not call reserve (useful if you do that in advance)
falign
buffer$append_byte_noreserve:
prolog buffer$append_byte_noreserve
mov rdx, [rdi+buffer_endptr_ofs]
mov byte [rdx], sil
add qword [rdi+buffer_endptr_ofs], 1
add qword [rdi+buffer_length_ofs], 1
epilog
end if
if used buffer$append_word | defined include_everything
; two arguments: rdi: buffer object, si: word to append
falign
buffer$append_word:
prolog buffer$append_word
push rdi rsi
mov esi, 2
call buffer$reserve
pop rsi rdi
mov rdx, [rdi+buffer_endptr_ofs]
mov word [rdx], si
add qword [rdi+buffer_endptr_ofs], 2
add qword [rdi+buffer_length_ofs], 2
epilog
end if
if used buffer$append_dword | defined include_everything
; two arguments: rdi: buffer object, esi: dword to append
falign
buffer$append_dword:
prolog buffer$append_dword
push rdi rsi
mov esi, 4
call buffer$reserve
pop rsi rdi
mov rdx, [rdi+buffer_endptr_ofs]
mov dword [rdx], esi
add qword [rdi+buffer_endptr_ofs], 4
add qword [rdi+buffer_length_ofs], 4
epilog
end if
if used buffer$append_qword | defined include_everything
; two arguments: rdi: buffer object, rsi: qword to append
falign
buffer$append_qword:
prolog buffer$append_qword
push rdi rsi
mov esi, 8
call buffer$reserve
pop rsi rdi
mov rdx, [rdi+buffer_endptr_ofs]
mov qword [rdx], rsi
add qword [rdi+buffer_endptr_ofs], 8
add qword [rdi+buffer_length_ofs], 8
epilog
end if
if used buffer$append_double | defined include_everything
; two arguments: rdi: buffer object, xmm0: double to append
falign
buffer$append_double:
prolog buffer$append_double
sub rsp, 16
mov [rsp], rdi
movq [rsp+8], xmm0
mov esi, 8
call buffer$reserve
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rdx, [rdi+buffer_endptr_ofs]
mov qword [rdx], rsi
add qword [rdi+buffer_endptr_ofs], 8
add qword [rdi+buffer_length_ofs], 8
epilog
end if
if used buffer$append_string | defined include_everything
; two arguments: rdi: buffer object, rsi: string object
; NOTE: does a utf8 conversion of the string object into the buffer
falign
buffer$append_string:
prolog buffer$append_string
if defined buffer_append_string_conservative
sub rsp, 24
mov [rsp], rdi
mov [rsp+8], rsi
mov rdi, rsi
call string$utf8_length
mov [rsp+16], rax
mov rsi, rax
mov rdi, [rsp]
call buffer$reserve
mov rsi, [rsp]
mov rsi, [rsi+buffer_endptr_ofs]
mov rdi, [rsp+8]
call string$to_utf8
mov rdi, [rsp]
mov rax, [rsp+16]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 24
epilog
else
; this version is considerably faster, but wastes space w/ buffer$reserve
push rbx rsi
mov rbx, rdi
mov rsi, [rsi]
if string_bits = 32
shl rsi, 2
else
shl rsi, 1
end if
call buffer$reserve
pop rdi
mov rsi, [rbx+buffer_endptr_ofs]
call string$to_utf8
add qword [rbx+buffer_endptr_ofs], rax
add qword [rbx+buffer_length_ofs], rax
pop rbx
epilog
end if
end if
if used buffer$append_rawstring_noreserve | defined include_everything
; two arguments: rdi == buffer object, rsi: string object
; NOTE: does not do a utf8 conversion, but takes characters only out of the string (does not call reserve either)
falign
buffer$append_rawstring_noreserve:
prolog buffer$append_rawstring_noreserve
cmp qword [rsi], 0
je .nothingtodo
mov rdx, [rsi]
add rsi, 8
if string_bits = 32
shl rdx, 2
else
shl rdx, 1
end if
; modified copy of buffer$append here:
mov rcx, rdi
mov rdi, [rdi+buffer_endptr_ofs]
add qword [rcx+buffer_endptr_ofs], rdx
add qword [rcx+buffer_length_ofs], rdx
call memcpy
epilog
calign
.nothingtodo:
epilog
end if
if used buffer$append_rawstring | defined include_everything
; two arguments: rdi == buffer object, rsi: string object
; NOTE: does not do a utf8 conversion, but takes the characters only out of the string
falign
buffer$append_rawstring:
prolog buffer$append_rawstring
cmp qword [rsi], 0
je .nothingtodo
mov rdx, [rsi]
add rsi, 8
if string_bits = 32
shl rdx, 2
else
shl rdx, 1
end if
call buffer$append
epilog
calign
.nothingtodo:
epilog
end if
if used buffer$append_hexdecode | defined include_everything
; two arguments: rdi: buffer object, rsi: string object that contains hex chars only
; NOTE: input must be sensible, but we skip whitespace (not _between_ 4 bits)
; returns # of bytes it added to the buffer
falign
buffer$append_hexdecode:
prolog buffer$append_hexdecode
push rsi rdi
mov rsi, [rsi] ; length of the string in characters
shr rsi, 1 ; in bytes that we'll need room for
call buffer$reserve
mov rdx, [rsp]
mov rdi, [rsp+8] ; source string
mov rsi, [rdx+buffer_endptr_ofs]
call string$hexdecode ; will return # of bytes it wrote
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 16
epilog
end if
if used buffer$append_hexdecode_latin1 | defined include_everything
; this is the same as above, only instead of the source being a native string
; it is a pointer/length that contains latin1 of the source hex
; three arguments: rdi: buffer object, rsi: pointer to buffer with latin1 hexchars, rdx: length of same
; NOTE: input must be sensible, but we skip whitespace (not _between_ 4 bits)
; returns # of bytes it added to the buffer
falign
buffer$append_hexdecode_latin1:
prolog buffer$append_hexdecode_latin1
xor eax, eax
test rdx, rdx
jz .nothingtodo
push rdx rsi rdi
mov rsi, rdx
shr rsi, 1 ; in bytes that we'll need room for
call buffer$reserve
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rcx, [rsp+16]
mov rdi, [rdi+buffer_itself_ofs]
xor eax, eax
calign
.doit:
movzx edx, word [rsi]
mov r8d, edx
and edx, 0xff
shr r8d, 8
sub rcx, 1
jz .bailout
cmp edx, 32
jbe .whitespaceordie
add rsi, 2
cmp edx, 48
jb .bailout
cmp r8d, 48
jb .bailout
cmp edx, 102
ja .bailout
cmp r8d, 102
ja .bailout
sub edx, 48
sub r8d, 48
mov r11d, edx
sub r11d, 39
cmp edx, 10
cmovb r9d, edx
cmovae r9d, r11d
test r9d, 0xf0
jnz .bailout
mov r11d, r8d
sub r11d, 39
cmp r8d, 10
cmovb r10d, r8d
cmovae r10d, r11d
test r10d, 0xf0
jnz .bailout
shl r9d, 4
or r9d, r10d
mov byte [rdi], r9b
add rdi, 1
add rax, 1
sub rcx, 1
jnz .doit
; else, done
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 24
epilog
calign
.whitespaceordie:
add rsi, 1
cmp edx, 32
je .doit
cmp edx, 13
je .doit
cmp edx, 10
je .doit
cmp edx, 9
je .doit
; fallthrough to bailout
calign
.bailout:
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 24
epilog
calign
.nothingtodo:
epilog
end if
if used buffer$append_hexencode_latin1 | defined include_everything
; this is similar to string$from_bintohex, only we don't bother with creating a native string
; and instead, write latin1 chars directly to our buffer
; three arguments: rdi: buffer object, rsi: pointer to byte buffer, rdx: length of same
; returns # of latin1 characters we added to the buffer
falign
buffer$append_hexencode_latin1:
prolog buffer$append_hexencode_latin1
test rdx, rdx
jz .nothingtodo
push rdx rsi rdi
mov rsi, rdx
shl rsi, 1 ; 2 characters per byte of input
call buffer$reserve
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rcx, [rsp+16]
mov rdi, [rdi+buffer_itself_ofs]
xor eax, eax
calign
.doit:
movzx edx, byte [rsi]
add rsi, 1
mov r8d, edx
and edx, 0xf
shr r8d, 4
if string_bits = 32
mov r9d, dword [rdx*4+.hexchars+8]
mov r10d, dword [r8*4+.hexchars+8]
else
movzx r9d, word [rdx*2+.hexchars+8]
movzx r10d, word [r8*2+.hexchars+8]
end if
shl r10d, 8
or r9d, r10d
mov word [rdi], r9w
add rdi, 2
add rax, 2
sub rcx, 1
jnz .doit
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 24
epilog
calign
.nothingtodo:
xor eax, eax
epilog
cleartext .hexchars, '0123456789abcdef'
end if
if used buffer$append_base64decode | defined include_everything
; three arguments: rdi: buffer object, rsi: string object that contains the base64 encoding, rdx == 0 == default base64 table, else rdx == custom base64 table
; returns # of bytes it added to the buffer
falign
buffer$append_base64decode:
prolog buffer$append_base64decode
push rdx rsi rdi
mov rdx, [rsi]
mov rsi, rdx
shr rdx, 2
sub rsi, rdx ; length in characters - 25% is how many bytes we'll need to reserve space for
add rsi, 32 ; plus a bit for good measure
call buffer$reserve
mov rdx, [rsp]
mov rdi, [rsp+8] ; source string
mov rsi, [rdx+buffer_endptr_ofs]
mov rdx, [rsp+16]
call string$base64decode ; will return # of bytes it wrote
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 24
epilog
end if
if used buffer$append_base64urldecode | defined include_everything
; three arguments: rdi: buffer object, rsi: string object that contains the base64 encoding, rdx == 0 == default base64 table, else rdx == custom base64 table
; returns # of bytes it added to the buffer
falign
buffer$append_base64urldecode:
prolog buffer$append_base64urldecode
push rdx rsi rdi
mov rdx, [rsi]
mov rsi, rdx
shr rdx, 2
sub rsi, rdx ; length in characters - 25% is how many bytes we'll need to reserve space for
add rsi, 32 ; plus a bit for good measure
call buffer$reserve
mov rdx, [rsp]
mov rdi, [rsp+8] ; source string
mov rsi, [rdx+buffer_endptr_ofs]
mov rdx, [rsp+16]
call string$base64urldecode ; will return # of bytes it wrote
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 24
epilog
end if
if used buffer$append_base64tobin_latin1 | defined include_everything
; four arguments: rdi: buffer object, rsi: pointer to buffer with latin1 base64 chars, rdx: length of same, rcx == 0 == default base64 table, else rcx == custom base64 table
; NOTE: we reserve the same # of bytes as the input, though wasteful... TODO: chop by 25% instead, which should still be safe
; returns # of bytes we added to the buffer
falign
buffer$append_base64tobin_latin1:
prolog buffer$append_base64tobin_latin1
sub rsp, 32
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+16], rdx
mov [rsp+24], rcx
mov rsi, rdx
call buffer$reserve
mov rdi, [rsp+8]
mov rsi, [rsp+16]
mov rcx, [rsp+24]
mov rdx, [rsp]
mov rdx, [rdx+buffer_itself_ofs]
call base64$decode_latin1
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 32
epilog
end if
if used buffer$append_base64urltobin_latin1 | defined include_everything
; four arguments: rdi: buffer object, rsi: pointer to buffer with latin1 base64 chars, rdx: length of same, rcx == 0 == default base64 table, else rcx == custom base64 table
; NOTE: we reserve the same # of bytes as the input, though wasteful... TODO: chop by 25% instead, which should still be safe
; returns # of bytes we added to the buffer
falign
buffer$append_base64urltobin_latin1:
prolog buffer$append_base64urltobin_latin1
sub rsp, 32
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+16], rdx
mov [rsp+24], rcx
mov rsi, rdx
call buffer$reserve
mov rdi, [rsp+8]
mov rsi, [rsp+16]
mov rcx, [rsp+24]
mov rdx, [rsp]
mov rdx, [rdx+buffer_itself_ofs]
call base64url$decode_latin1
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 32
epilog
end if
if used buffer$append_bintobase64_latin1 | defined include_everything
; this is similar to string$from_bintobase64, only we don't bother with creating a native string
; and instead write latin1 base64 chars directly to the buffer
; four arguments: rdi: buffer object, rsi: pointer to byte buffer, rdx: length of same, rcx == 0 == default base64 table, else, rcx == STRING custom base64 table
; returns # of latin1 base64 characters we added to the buffer
falign
buffer$append_bintobase64_latin1:
prolog buffer$append_bintobase64_latin1
sub rsp, 32
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+16], rdx
mov [rsp+24], rcx
mov rdi, rdx
call base64$encode_length
mov rdi, [rsp]
mov rsi, rax
call buffer$reserve
mov rdi, [rsp+8]
mov rsi, [rsp+16]
mov rdx, [rsp]
mov rcx, [rsp+24]
mov rdx, [rdx+buffer_itself_ofs]
call base64$encode_latin1
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 32
epilog
end if
if used buffer$append_bintobase64url_latin1 | defined include_everything
; this is similar to string$from_bintobase64, only we don't bother with creating a native string
; and instead write latin1 base64 chars directly to the buffer
; four arguments: rdi: buffer object, rsi: pointer to byte buffer, rdx: length of same, rcx == 0 == default base64 table, else, rcx == STRING custom base64 table
; returns # of latin1 base64 characters we added to the buffer
falign
buffer$append_bintobase64url_latin1:
prolog buffer$append_bintobase64url_latin1
sub rsp, 32
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+16], rdx
mov [rsp+24], rcx
mov rdi, rdx
call base64$encode_length
mov rdi, [rsp]
mov rsi, rax
call buffer$reserve
mov rdi, [rsp+8]
mov rsi, [rsp+16]
mov rdx, [rsp]
mov rcx, [rsp+24]
mov rdx, [rdx+buffer_itself_ofs]
call base64url$encode_latin1
mov rdi, [rsp]
add qword [rdi+buffer_endptr_ofs], rax
add qword [rdi+buffer_length_ofs], rax
add rsp, 32
epilog
end if
if used buffer$has_more_lines | defined include_everything
; two arguments: rdi: buffer object, bool in esi as to whether or not we should consume leading empty lines (you probably want this)
; TODO: make this multibyte instead of the slow char-by-char method, lazy boy.
falign
buffer$has_more_lines:
prolog buffer$has_more_lines
test esi, esi
jnz .consumeleadingempties
mov rsi, [rdi+buffer_itself_ofs]
mov rdx, [rdi+buffer_endptr_ofs]
cmp rsi, rdx
je .falseret
calign
.while:
cmp rsi, rdx
jae .falseret
mov al, byte [rsi]
cmp al, 13
je .trueret
cmp al, 10
je .trueret
add rsi, 1
jmp .while
calign
.falseret:
xor eax, eax
epilog
calign
.trueret:
mov eax, 1
epilog
calign
.consumeleadingempties:
mov rsi, [rdi+buffer_itself_ofs]
mov rdx, [rdi+buffer_endptr_ofs]
cmp rsi, rdx
je .falseret
calign
.consumewhile:
cmp rsi, rdx
jae .consumewhiledone
mov al, byte [rsi]
cmp al, 13
je .consumewhilenext
cmp al, 10
je .consumewhilenext
; else, consumewhiledone:
cmp rsi, [rdi+buffer_itself_ofs]
jne .doconsume
cmp rsi, rdx
jae .falseret
jmp .while
calign
.consumewhilenext:
add rsi, 1
jmp .consumewhile
calign
.consumewhiledone:
cmp rsi, [rdi+buffer_itself_ofs]
jne .doconsume
cmp rsi, rdx
jae .falseret
jmp .while
calign
.doconsume:
push rdi
sub rsi, qword [rdi+buffer_itself_ofs]
call buffer$consume
pop rdi
mov rsi, [rdi+buffer_itself_ofs]
mov rdx, [rdi+buffer_endptr_ofs]
cmp rsi, rdx
jae .falseret
jmp .while
end if
if used buffer$check_last_lf | defined include_everything
; single arg in rdi: buffer object
; if length is nonzero, makes sure that the last character in the buffer is a 10
; and if not, adds one
falign
buffer$check_last_lf:
prolog buffer$check_last_lf
mov rsi, [rdi+buffer_itself_ofs]
mov rdx, [rdi+buffer_length_ofs]
test rdx, rdx
jz .nothingtodo
add rsi, rdx
sub rsi, 1
cmp byte [rsi], 10
je .nothingtodo
mov esi, 10
call buffer$append_byte
epilog
calign
.nothingtodo:
epilog
end if
if used buffer$nextline | defined include_everything
; single arg in rdi: buffer object
; returns a _new_ (heap$alloc'd) string in rax and consumes through the linefeed
falign
buffer$nextline:
prolog buffer$nextline
mov rsi, [rdi+buffer_itself_ofs]
mov rdx, [rdi+buffer_endptr_ofs]
cmp rsi, rdx
je .emptystring
calign
.while:
cmp rsi, rdx
jae .emptystring
mov al, byte [rsi]
cmp al, 13
je .doit
cmp al, 10
je .doit
add rsi, 1
jmp .while
calign
.emptystring:
call string$new
epilog
calign
.doit:
sub rsp, 24
mov [rsp], rdi
mov rdi, qword [rdi+buffer_itself_ofs] ; buffer start
sub rsi, rdi ; length in bytes
mov [rsp+8], rsi
call string$from_utf8
mov [rsp+16], rax ; save our return string
mov rdi, [rsp]
mov rdx, [rsp+8]
mov rcx, [rdi+buffer_length_ofs]
mov rsi, [rdi+buffer_itself_ofs]
calign
.consumewhile:
cmp rdx, rcx
jae .resetonly
cmp byte [rsi+rdx], 13
je .consumewhilenext
cmp byte [rsi+rdx], 10
je .consumewhilenext
; else, our consume ends here, rdx has # of bytes to consume
mov rsi, rdx
call buffer$consume
mov rax, [rsp+16] ; our return
add rsp, 24
epilog
calign
.consumewhilenext:
add rdx, 1
jmp .consumewhile
calign
.resetonly:
call buffer$reset
mov rax, [rsp+16]
add rsp, 24
epilog
end if
if used buffer$cdebug | defined include_everything
; single argument in rdi == our buffer object
; this dumps to stdout a c array, useful for external debugging/etc
falign
buffer$cdebug:
prolog buffer$cdebug
push rbx r12 r13
mov rbx, rdi
mov rdi, .pref1
call string$to_stdout
mov rdi, [rbx+buffer_length_ofs]
mov esi, 10
call string$from_unsigned
push rax
mov rdi, rax
call string$to_stdout
pop rdi
call heap$free
mov rdi, .pref2
call string$to_stdout
mov r12, [rbx+buffer_length_ofs]
test r12, r12
jz .empty
mov r13, [rbx+buffer_itself_ofs]
calign
.doit:
mov rdi, .pref3
mov rsi, .pref4
cmp r13, [rbx+buffer_itself_ofs]
cmovne rdi, rsi
call string$to_stdout
movzx edi, byte [r13]
mov esi, 16
call string$from_unsigned
push rax
mov rdi, rax
call string$to_stdout
pop rdi
call heap$free
add r13, 1
sub r12, 1
jnz .doit
calign
.empty:
mov rdi, .end
call string$to_stdoutln
pop r13 r12 rbx
epilog
cleartext .pref1, 'unsigned char buffer['
cleartext .pref2, '] = { '
cleartext .pref3, '0x'
cleartext .pref4, ', 0x'
cleartext .end, ' };'
end if
if used buffer$file_write | defined include_everything
; two arguments: rdi == buffer object, rsi == filename to write to
; NOTE: does no conversion, writes the buffer as-is to the file
; also turns rsi into a null terminated UTF8 suitable for syscall use
; returns # of bytes written in rax
falign
buffer$file_write:
prolog buffer$file_write
cmp qword [rdi+buffer_length_ofs], 0
je .nothingtodo
push rbx r12
mov r12, rdi
mov rbx, rsi
mov rdi, rsi
call string$utf8_length
mov rdi, rbx
mov rbx, rax
add rbx, 16
and rbx, not 15
sub rsp, rbx
mov rsi, rsp
mov byte [rsp+rax], 0
call string$to_utf8
mov rdi, r12
mov rsi, rsp
call buffer$file_write_cstr
add rsp, rbx
pop r12 rbx
epilog
calign
.nothingtodo:
epilog
end if
if used buffer$file_write_cstr | defined include_everything
; two arguments: rdi == buffer object, rsi == null terminated latin1 filename
; returns # of bytes written in rax
falign
buffer$file_write_cstr:
prolog buffer$file_write_cstr
cmp qword [rdi+buffer_length_ofs], 0
je .nothingtodo
push rbx r12
mov rbx, rdi
mov eax, syscall_open
mov rdi, rsi
mov esi, 0x242 ; O_RDWR | O_CREAT | O_TRUNC
mov edx, 0x1b6 ; mode == 666
syscall
mov r12, rax
cmp eax, 0
jl .kakked
mov rdi, rax
mov eax, syscall_write
mov rsi, [rbx+buffer_itself_ofs]
mov rdx, [rbx+buffer_length_ofs]
syscall
mov rdi, r12
mov r12, rax
mov eax, syscall_close
syscall
mov rax, r12
pop r12 rbx
epilog
calign
.kakked:
pop r12 rbx
epilog
calign
.nothingtodo:
epilog
end if
if used buffer$file_append | defined include_everything
; two arguments: rdi == buffer object, rsi == filename to append to
; NOTE: does no conversion, appends the buffer as-is to the file
; also turns rsi into a null terminated UTF8 suitable for syscall use
; returns # of bytes written in rax
falign
buffer$file_append:
prolog buffer$file_append
cmp qword [rdi+buffer_length_ofs], 0
je .nothingtodo
push rbx r12
mov r12, rdi
mov rbx, rsi
mov rdi, rsi
call string$utf8_length
mov rdi, rbx
mov rbx, rax
add rbx, 16
and rbx, not 15
sub rsp, rbx
mov rsi, rsp
mov byte [rsp+rax], 0
call string$to_utf8
mov rdi, r12
mov rsi, rsp
call buffer$file_append_cstr
add rsp, rbx
pop r12 rbx
calign
.nothingtodo:
epilog
end if
if used buffer$file_append_cstr | defined include_everything
; two arguments: rdi == buffer object, rsi == null terminated latin1 filename
; returns # of bytes written in rax
falign
buffer$file_append_cstr:
prolog buffer$file_append_cstr
cmp qword [rdi+buffer_length_ofs], 0
je .nothingtodo
push rbx r12
mov rbx, rdi
mov eax, syscall_open
mov rdi, rsi
mov esi, 0x442 ; O_RDWR | O_CREAT | O_APPEND
mov edx, 0x1b6 ; mode == 666
syscall
mov r12, rax
cmp eax, 0
jl .kakked
mov rdi, rax
mov eax, syscall_write
mov rsi, [rbx+buffer_itself_ofs]
mov rdx, [rbx+buffer_length_ofs]
syscall
mov rdi, r12
mov r12, rax
mov eax, syscall_close
syscall
mov rax, r12
pop r12 rbx
epilog
calign
.kakked:
pop r12 rbx
epilog
calign
.nothingtodo:
epilog
end if