; ------------------------------------------------------------------------
; 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/>.
; ------------------------------------------------------------------------
;
; url.inc: URL string parsing and relative handling, as well as url encode/decode
;
; This is not intended to be the end-all-be-all in RFC compliance, but does the job
; that I require nicely. Specifically:
; During parse, we don't percent decode the protocol (aka scheme), host, port, or the
; authinfo (aka username and password)
; The same applies to when we turn it back into a string form with url$tostring.
; Also, we don't deal with path parameters (any such will just end up as part of the
; path/file).
;
; we populate all strings such that they don't need to be tested for null
; (though of course they may be empty)
;
url_protocol_ofs = 0 ; string
url_host_ofs = 8 ; string
url_port_ofs = 16 ; int
url_file_ofs = 24 ; string
url_query_ofs = 32 ; string
url_authority_ofs = 40 ; string
url_path_ofs = 48 ; string
url_authinfo_ofs = 56 ; string
url_ref_ofs = 64 ; string
url_user_ofs = 72 ; not used in here, but we initialize it to zero on alloc
url_size = 80
if used url$init | defined include_everything
globals
{
url$schemeportmap dq 0
}
; no arguments, called from ht$init to initialize our default ports for the schemes we are about
; this list is intentionally short based on my own personal use cases.
falign
url$init:
prolog url$init
xor edi, edi
call stringmap$new
mov [url$schemeportmap], rax
macro url_addone port*, [val*] {
common
local .NAME, .POST
jmp .POST
cleartext .NAME, val
.POST:
mov rdi, [url$schemeportmap]
mov rsi, .NAME
mov edx, port
call stringmap$insert_unique
}
url_addone 80, 'http'
url_addone 443, 'https'
url_addone 21, 'ftp'
url_addone 3690, 'svn'
url_addone 9418, 'git'
url_addone 6667, 'irc'
url_addone 143, 'imap'
url_addone 119, 'nntp'
epilog
end if
if used url$new | defined include_everything
; two arguments: rdi == optional base context url, rsi == string
; returns new url object in rax, or null on error (e.g. no context and /some.path passed in == error, bad port)
falign
url$new:
prolog url$new
mov eax, [rsi]
if string_bits = 32
mov ecx, [rsi+8]
else
movzx ecx, word [rsi+8]
end if
push rbx r12 r13
mov rbx, rdi
mov r12, rsi
xor r13d, r13d
push r14 r15
sub rsp, 128
mov r14d, eax
test eax, eax
jz .initial_nullret
sub eax, 1
if string_bits = 32
mov edx, [rsi+rax*4+8]
else
movzx edx, word [rsi+rax*2+8]
end if
cmp ecx, 32
jbe .chopspaces
cmp edx, 32
jbe .chopspaces
calign
.checkurlstart:
mov rdi, r12
mov rsi, .urlcolon
mov edx, r13d
call string$indexof_ofs
lea edx, [r13d+4]
cmp rax, r13
cmove r13d, edx
cmp r13d, r14d
jae .initial_nullret
call string$new
mov [rsp+url_protocol_ofs], rax
if string_bits = 32
cmp dword [r12+r13*4+8], '#'
else
cmp word [r12+r13*2+8], '#'
end if
je .checkprotocol
mov r15d, r13d
calign
.protoloop:
cmp r15d, r14d
jae .checkprotocol
if string_bits = 32
mov eax, [r12+r15*4+8]
else
movzx eax, word [r12+r15*2+8]
end if
cmp eax, '/'
je .checkprotocol
cmp eax, ':'
je .protoloop_found
add r15d, 1
jmp .protoloop
cleartext .urlcolon, 'url:'
calign
.chopspaces:
.trailing_ws:
test r14d, r14d
jz .initial_nullret
mov rdi, r12
mov esi, r14d
sub esi, 1
call string$charat
cmp eax, 32
ja .leading_ws
sub r14d, 1
jnz .trailing_ws
jz .initial_nullret
calign
.leading_ws:
cmp r13d, r14d
jae .initial_nullret
mov rdi, r12
mov esi, r13d
call string$charat
cmp eax, 32
ja .checkurlstart
add r13d, 1
jmp .leading_ws
calign
.protoloop_found:
mov rdi, r12
mov esi, r13d
mov edx, r15d
call string$substring
push rax
mov rdi, rax
call string$to_lower_inplace
mov rdi, [rsp+8]
call heap$free
pop rdi
mov [rsp+url_protocol_ofs], rdi
mov r13d, r15d
add r13d, 1
calign
.checkprotocol:
; so at this point we have a possible protocol string sitting in [rsp]
test rbx, rbx
jz .verifyprotocol
mov rdi, [rsp+url_protocol_ofs]
mov rsi, [rbx+url_protocol_ofs]
call string$equals
mov rsi, [rsp+url_protocol_ofs]
xor ecx, ecx
mov edx, 1
cmp qword [rsi], 0
cmove ecx, edx
or eax, ecx
jz .verifyprotocol
mov rdi, [rbx+url_path_ofs]
mov esi, '/'
call string$indexof_charcode
test rax, rax
jnz .checkemptyproto
; otherwise, set it to empty
call string$new
mov rdi, [rsp]
mov [rsp+url_protocol_ofs], rax
call heap$free
calign
.checkemptyproto:
mov rdi, [rsp+url_protocol_ofs]
cmp qword [rdi], 0
jne .verifyprotocol
mov rdi, [rbx+url_protocol_ofs]
cmp qword [rdi], 0
je .protocol_nullret
; otherwise, set us up for full relative action
call string$copy
mov rdi, [rsp+url_protocol_ofs]
mov [rsp+url_protocol_ofs], rax
call heap$free
; set host, port, file, authority, path, authinfo
mov rdi, [rbx+url_host_ofs]
call string$copy
mov [rsp+url_host_ofs], rax
mov rdi, [rbx+url_port_ofs]
mov [rsp+url_port_ofs], rdi
mov rdi, [rbx+url_file_ofs]
call string$copy
mov [rsp+url_file_ofs], rax
mov rdi, [rbx+url_authority_ofs]
call string$copy
mov [rsp+url_authority_ofs], rax
mov rdi, [rbx+url_path_ofs]
call string$copy
mov [rsp+url_path_ofs], rax
mov rdi, [rbx+url_authinfo_ofs]
call string$copy
mov [rsp+url_authinfo_ofs], rax
; now, query and ref still need set
mov rdi, r12
mov esi, '#'
mov edx, r13d
call string$indexof_charcode_ofs
cmp rax, -1
jne .relative_withref
call string$new
mov [rsp+url_ref_ofs], rax
cmp r13d, r14d
je .relative_withquery
call string$new
mov [rsp+url_query_ofs], rax
jmp .parseurl
calign
.relative_withquery:
mov rdi, [rbx+url_query_ofs]
call string$copy
mov [rsp+url_query_ofs], rax
mov rdi, [rsp+url_ref_ofs]
cmp qword [rdi], 0
jne .parseurl
mov rdi, [rsp+url_ref_ofs]
call heap$free
mov rdi, [rbx+url_ref_ofs]
call string$copy
mov [rsp+url_ref_ofs], rax
jmp .parseurl
calign
.relative_withref:
mov rdi, r12
mov esi, eax
mov edx, r14d
mov r14d, eax
add esi, 1
call string$substring
mov [rsp+url_ref_ofs], rax
cmp r13d, r14d
jne .relative_emptyquery
mov rdi, [rbx+url_query_ofs]
call string$copy
mov [rsp+url_query_ofs], rax
mov rdi, [rsp+url_ref_ofs]
cmp qword [rdi], 0
jne .parseurl
mov rdi, [rsp+url_ref_ofs]
call heap$free
mov rdi, [rbx+url_ref_ofs]
call string$copy
mov [rsp+url_ref_ofs], rax
jmp .parseurl
calign
.relative_emptyquery:
call string$new
mov [rsp+url_query_ofs], rax
jmp .parseurl
calign
.verifyprotocol:
mov rdi, [rsp+url_protocol_ofs]
cmp qword [rdi], 0
je .protocol_nullret
mov rdi, r12
mov esi, '#'
mov edx, r13d
call string$indexof_charcode_ofs
cmp rax, -1
je .empty_ref
mov rdi, r12
mov esi, eax
mov edx, r14d
mov r14d, eax
add esi, 1
call string$substring
mov [rsp+url_ref_ofs], rax
jmp .notrelative_empties
calign
.empty_ref:
call string$new
mov [rsp+url_ref_ofs], rax
; fallthrough to notrelative_empties
calign
.notrelative_empties:
; so at this point, we have protocol and ref setup, we need to set the rest
; to all empty strings
call string$new
mov [rsp+url_host_ofs], rax
mov qword [rsp+url_port_ofs], 0
call string$new
mov [rsp+url_file_ofs], rax
call string$new
mov [rsp+url_query_ofs], rax
call string$new
mov [rsp+url_authority_ofs], rax
call string$new
mov [rsp+url_path_ofs], rax
call string$new
mov [rsp+url_authinfo_ofs], rax
; fallthrough to parseurl
calign
.parseurl:
; let the meaty bits begin
mov qword [rsp+72], 0
cmp r13d, r14d
ja .parseurl_checkstart
mov rdi, r12
mov esi, '?'
mov edx, r13d
call string$indexof_charcode_ofs
xor ecx, ecx
mov edx, 1
cmp rax, r13
cmove ecx, edx
mov [rsp+76], ecx ; are we only sitting on a query?
cmp rax, -1
je .parseurl_checkstart
cmp eax, r14d
jae .parseurl_checkstart
mov rdi, r12
mov esi, eax
mov edx, r14d
cmp r14d, eax
cmova r14d, eax
add esi, 1
call string$substring
mov rdi, [rsp+url_query_ofs]
mov [rsp+url_query_ofs], rax
call heap$free
calign
.parseurl_checkstart:
mov eax, r14d
sub eax, r13d
cmp eax, 4
jb .parseurl_checkpath
if string_bits = 32
cmp dword [r12+r13*4+8], '/'
else
cmp word [r12+r13*2+8], '/'
end if
jne .parseurl_checkpath
if string_bits = 32
cmp dword [r12+r13*4+12], '/'
else
cmp word [r12+r13*2+10], '/'
end if
jne .parseurl_checkpath
if string_bits = 32
cmp dword [r12+r13*4+16], '/'
else
cmp word [r12+r13*2+12], '/'
end if
je .parseurl_checkpath
; otherwise, we got // as start, do our authority
add r13d, 2
mov rdi, r12
mov esi, '/'
mov edx, r13d
call string$indexof_charcode_ofs
mov r15, rax
cmp rax, 0
jge .parseurl_setauthority
mov rdi, r12
mov esi, '?'
mov edx, r13d
call string$indexof_charcode_ofs
mov r15, rax
cmp rax, 0
jge .parseurl_setauthority
mov r15d, r14d
calign
.parseurl_setauthority:
mov rdi, r12
mov esi, r13d
mov edx, r15d
call string$substring
mov rdi, [rsp+url_host_ofs]
mov [rsp+url_host_ofs], rax
call heap$free
mov rdi, [rsp+url_authority_ofs]
call heap$free
mov rdi, [rsp+url_host_ofs]
call string$copy
mov [rsp+url_authority_ofs], rax
mov rdi, rax
mov esi, '@'
call string$indexof_charcode
cmp rax, 0
jl .parseurl_setauthinfo_empty
mov [rsp+80], rax
mov rdi, [rsp+url_authority_ofs]
mov esi, 0
mov edx, eax
call string$substr
mov rdi, [rsp+url_authinfo_ofs]
mov [rsp+url_authinfo_ofs], rax
call heap$free
mov rdi, [rsp+url_authority_ofs]
mov esi, [rsp+80]
mov rdx, -1
add esi, 1
call string$substr
mov rdi, [rsp+url_host_ofs]
mov [rsp+url_host_ofs], rax
call heap$free
jmp .parseurl_checkhost
calign
.parseurl_setauthinfo_empty:
call string$new
mov rdi, [rsp+url_authinfo_ofs]
mov [rsp+url_authinfo_ofs], rax
call heap$free
calign
.parseurl_checkhost:
mov rdi, [rsp+url_host_ofs]
cmp qword [rdi], 0
je .parseurl_checkport
; hosts need to be lowercase, authority doesn't
call string$to_lower_inplace
mov rdi, [rsp+url_host_ofs]
; if we were going to check for IPv6 hosts, here would be the place if host.charat(0) == '['
mov esi, ':'
call string$indexof_charcode
cmp rax, 0
jl .parseurl_checkport
mov [rsp+80], rax
mov rdi, [rsp+url_host_ofs]
mov esi, eax
add esi, 1
cmp qword [rdi], rsi
jbe .parseurl_chophost
mov rdx, -1
call string$substr
mov [rsp+88], rax
mov rdi, rax
call string$to_unsigned
mov rdi, [rsp+88]
mov [rsp+url_port_ofs], rax
call heap$free
calign
.parseurl_chophost:
mov rdi, [rsp+url_host_ofs]
xor esi, esi
mov rdx, [rsp+80]
call string$substr
mov rdi, [rsp+url_host_ofs]
mov [rsp+url_host_ofs], rax
call heap$free
calign
.parseurl_checkport:
mov eax, [rsp+url_port_ofs]
cmp eax, 0
je .parseurl_defaultport
jl .badport_nullret
cmp eax, 65536
jae .badport_nullret
calign
.parseurl_checkport_okay:
mov r13d, r15d
mov rdi, [rsp+url_authority_ofs]
cmp qword [rdi], 0
je .parseurl_checkpath
call string$new
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
calign
.parseurl_checkpath:
cmp r13d, r14d
jae .parseurl_checkpath_qonly
if string_bits = 32
cmp dword [r12+r13*4+8], '/'
else
cmp word [r12+r13*2+8], '/'
end if
jne .parseurl_relativepath
mov rdi, r12
mov esi, r13d
mov edx, r14d
call string$substring
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
jmp .parseurl_construct
calign
.parseurl_defaultport:
mov rdi, [url$schemeportmap]
mov rsi, [rsp+url_protocol_ofs]
call stringmap$find_value
test eax, eax
jz .badport_nullret
mov [rsp+url_port_ofs], edx
jmp .parseurl_checkport_okay
cleartext .emptystr, ''
cleartext .slash, '/'
cleartext .slashslash, '//'
cleartext .dotslash, './'
cleartext .slashdot, '/.'
cleartext .slashdotslash, '/./'
cleartext .slashdotdot, '/..'
cleartext .slashdotdotslash, '/../'
cleartext .percentdoublezero, '%00'
calign
.parseurl_relativepath:
mov rdi, [rsp+url_path_ofs]
cmp qword [rdi], 0
je .parseurl_setpath
; otherwise, dealing with a relative path
mov rsi, .slash
call string$last_indexof
cmp rax, 0
jl .parseurl_setpath
; otherwise, substr that + the new
mov rdi, [rsp+url_path_ofs]
xor esi, esi
mov rdx, rax
add rdx, 1
call string$substr
mov [rsp+80], rax
mov rdi, r12
mov esi, r13d
mov edx, r14d
call string$substring
mov [rsp+88], rax
mov rdi, [rsp+80]
mov rsi, rax
call string$concat
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
mov rdi, [rsp+80]
call heap$free
mov rdi, [rsp+88]
call heap$free
; so now, proceed to construct where we'll fixup any nasties in the path
jmp .parseurl_construct
calign
.parseurl_checkpath_qonly:
cmp dword [rsp+76], 1
jne .parseurl_construct
mov rdi, [rsp+url_path_ofs]
cmp qword [rdi], 0
je .parseurl_construct
mov esi, '/'
call string$indexof_charcode
xor ecx, ecx
cmp rax, 0
cmovl rax, rcx
mov rdi, [rsp+url_path_ofs]
xor esi, esi
mov rdx, rax
call string$substr
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
mov rdi, [rsp+url_path_ofs]
mov rsi, .slash
call string$concat
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
jmp .parseurl_construct
calign
.parseurl_setpath:
mov rdi, [rsp+url_authority_ofs]
cmp qword [rdi], 0
je .parseurl_setpath_nosep
mov rdi, r12
mov esi, r13d
mov edx, r14d
call string$substring
mov [rsp+88], rax
mov rdi, .slash
mov rsi, rax
call string$concat
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
mov rdi, [rsp+88]
call heap$free
jmp .parseurl_construct
calign
.parseurl_setpath_nosep:
mov rdi, r12
mov esi, r13d
mov edx, r14d
call string$substring
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
calign
.parseurl_construct:
; so before we construct our final return url object:
; first, we have to urldecode the relevant bits of our url
; if the path contains %00, die
mov rdi, [rsp+url_path_ofs]
mov rsi, .percentdoublezero
call string$indexof
cmp rax, 0
jge .badport_nullret
mov rdi, [rsp+url_path_ofs]
call url$decode_path
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
if defined url_querystring_decode
mov rdi, [rsp+url_query_ofs]
call url$decode
mov rdi, [rsp+url_query_ofs]
mov [rsp+url_query_ofs], rax
call heap$free
end if
mov rdi, [rsp+url_ref_ofs]
call url$decode
mov rdi, [rsp+url_ref_ofs]
mov [rsp+url_ref_ofs], rax
call heap$free
; make sure the path is sane (removing ../../.. etc)
; string$replace is nasty, so only do this if it actually contains a violator
xor r15d, r15d
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashslash
call string$indexof
cmp rax, 0
jl .parseurl_sanepath
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashslash
mov rdx, .slash
call string$replace
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
calign
.parseurl_sanepath:
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashdotslash
call string$indexof
cmp rax, 0
jl .parseurl_sanepath2
mov [rsp+80], rax
mov rdi, [rsp+url_path_ofs]
xor esi, esi
mov rdx, rax
call string$substr
mov [rsp+88], rax
mov rdi, [rsp+url_path_ofs]
mov rsi, [rsp+80]
mov rdx, -1
add rsi, 2
call string$substr
mov [rsp+80], rax
mov rdi, [rsp+88]
mov rsi, rax
call string$concat
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
mov rdi, [rsp+88]
call heap$free
mov rdi, [rsp+80]
call heap$free
jmp .parseurl_sanepath
calign
.parseurl_sanepath2:
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashdotdotslash
mov edx, r15d
call string$indexof_ofs
mov r15, rax
cmp rax, 0
jl .parseurl_sanepath3
test rax, rax
jz .parseurl_sanepath2_skip
mov rdi, [rsp+url_path_ofs]
mov rsi, .slash
mov edx, r15d
sub edx, 1
call string$last_indexof_ofs
mov r14, rax
cmp rax, 0
jl .parseurl_sanepath2_skip
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashdotdotslash
mov edx, r14d
call string$indexof_ofs
test rax, rax
jz .parseurl_sanepath2_skip
mov rdi, [rsp+url_path_ofs]
xor esi, esi
mov edx, r14d
call string$substr
mov [rsp+80], rax
mov rdi, [rsp+url_path_ofs]
mov esi, r15d
mov rdx, -1
add esi, 3
call string$substr
mov [rsp+88], rax
mov rdi, [rsp+80]
mov rsi, rax
call string$concat
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
mov rdi, [rsp+80]
call heap$free
mov rdi, [rsp+88]
call heap$free
xor r15d, r15d
jmp .parseurl_sanepath2
calign
.parseurl_sanepath2_skip:
add r15d, 3
jmp .parseurl_sanepath2
calign
.parseurl_sanepath3:
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashdotdot
call string$ends_with
test rax, rax
jz .parseurl_sanepath4
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashdotdot
call string$indexof
mov r15, rax
mov rdi, [rsp+url_path_ofs]
mov rsi, .slash
mov edx, r15d
sub edx, 1
call string$last_indexof_ofs
cmp rax, 0
jl .parseurl_sanepath4
mov rdi, [rsp+url_path_ofs]
xor esi, esi
mov rdx, rax
add rdx, 1
call string$substr
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
jmp .parseurl_sanepath3
calign
.parseurl_sanepath4:
mov rdi, [rsp+url_path_ofs]
mov rsi, .dotslash
call string$starts_with
test rax, rax
jz .parseurl_sanepath5
mov rdi, [rsp+url_path_ofs]
mov esi, 2
mov rdx, -1
cmp qword [rdi], 2
jbe .parseurl_sanepath5
call string$substr
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
calign
.parseurl_sanepath5:
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashdot
call string$ends_with
test rax, rax
jz .parseurl_sanepath6
mov rdi, [rsp+url_path_ofs]
xor esi, esi
mov rdx, [rdi]
sub rdx, 1
call string$substr
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
calign
.parseurl_sanepath6:
; any starting backpath references can simply be deleted
mov rdi, [rsp+url_path_ofs]
mov rsi, .slashdotdot
call string$starts_with
test rax, rax
jz .parseurl_setfile
mov rdi, [rsp+url_path_ofs]
mov esi, 3
mov rdx, -1
call string$substr
mov rdi, [rsp+url_path_ofs]
mov [rsp+url_path_ofs], rax
call heap$free
jmp .parseurl_sanepath6
calign
.parseurl_setfile:
; and last but not least, we have to set our file to whatever the last bit of the path is
mov rdi, [rsp+url_path_ofs]
mov rsi, .slash
call string$last_indexof
cmp rax, 0
jl .parseurl_final
mov rdi, [rsp+url_path_ofs]
mov rsi, rax
mov rdx, -1
add rsi, 1
call string$substr
mov rdi, [rsp+url_file_ofs]
mov [rsp+url_file_ofs], rax
call heap$free
calign
.parseurl_final:
mov edi, url_size
call heap$alloc_clear
mov rbx, rax
mov rdi, rax
mov rsi, rsp
mov edx, url_size - 8 ; don't write over the user offset (leave it at zero)
call memcpy
mov rax, rbx
add rsp, 128
pop r15 r14 r13 r12 rbx
epilog
calign
.badport_nullret:
; free all of our strings
mov rdi, [rsp+url_host_ofs]
call heap$free
mov rdi, [rsp+url_file_ofs]
call heap$free
mov rdi, [rsp+url_query_ofs]
call heap$free
mov rdi, [rsp+url_authority_ofs]
call heap$free
mov rdi, [rsp+url_path_ofs]
call heap$free
mov rdi, [rsp+url_authinfo_ofs]
call heap$free
mov rdi, [rsp+url_ref_ofs]
call heap$free
; fall through
calign
.protocol_nullret:
mov rdi, [rsp+url_protocol_ofs]
call heap$free
; fallthrough
calign
.initial_nullret:
xor eax, eax
add rsp, 128
pop r15 r14 r13 r12 rbx
epilog
end if
if used url$destroy | defined include_everything
; single argument in rdi: the url object to destroy
falign
url$destroy:
prolog url$destroy
push rbx
mov rbx, rdi
mov rdi, [rdi+url_protocol_ofs]
call heap$free
mov rdi, [rbx+url_host_ofs]
call heap$free
mov rdi, [rbx+url_file_ofs]
call heap$free
mov rdi, [rbx+url_query_ofs]
call heap$free
mov rdi, [rbx+url_authority_ofs]
call heap$free
mov rdi, [rbx+url_path_ofs]
call heap$free
mov rdi, [rbx+url_authinfo_ofs]
call heap$free
mov rdi, [rbx+url_ref_ofs]
call heap$free
mov rdi, rbx
call heap$free
pop rbx
epilog
end if
if used url$equals | defined include_everything
; two arguments: rdi == url object, rsi == other url object to compare
falign
url$equals:
prolog url$equals
push r12 r13
mov r12, rdi
mov r13, rsi
mov eax, [r12+url_port_ofs]
cmp eax, [r13+url_port_ofs]
jne .nope
mov rdi, [r12+url_protocol_ofs]
mov rsi, [r13+url_protocol_ofs]
call string$equals
test eax, eax
jz .nope
mov rdi, [r12+url_host_ofs]
mov rsi, [r13+url_host_ofs]
call string$equals
test eax, eax
jz .nope
mov rdi, [r12+url_file_ofs]
mov rsi, [r13+url_file_ofs]
call string$equals
test eax, eax
jz .nope
mov rdi, [r12+url_query_ofs]
mov rsi, [r13+url_query_ofs]
call string$equals
test eax, eax
jz .nope
mov rdi, [r12+url_path_ofs]
mov rsi, [r13+url_path_ofs]
call string$equals
test eax, eax
jz .nope
mov eax, 1
pop r13 r12
epilog
calign
.nope:
xor eax, eax
pop r13 r12
epilog
end if
if used url$debug | defined include_everything
; single argument in rdi: a url object
; just dumps everything out to stdout
falign
url$debug:
prolog url$debug
push rbx
mov rbx, rdi
mov rdi, .pref
call string$to_stdoutln
mov rdi, .s1
call string$to_stdout
mov rdi, [rbx+url_protocol_ofs]
call string$to_stdoutln
mov rdi, .s2
call string$to_stdout
mov rdi, [rbx+url_host_ofs]
call string$to_stdoutln
mov rdi, .s3
call string$to_stdout
mov rdi, [rbx+url_port_ofs]
mov esi, 10
call string$from_unsigned
push rax
mov rdi, rax
call string$to_stdoutln
pop rdi
call heap$free
mov rdi, .s4
call string$to_stdout
mov rdi, [rbx+url_file_ofs]
call string$to_stdoutln
mov rdi, .s5
call string$to_stdout
mov rdi, [rbx+url_query_ofs]
call string$to_stdoutln
mov rdi, .s6
call string$to_stdout
mov rdi, [rbx+url_authority_ofs]
call string$to_stdoutln
mov rdi, .s7
call string$to_stdout
mov rdi, [rbx+url_path_ofs]
call string$to_stdoutln
mov rdi, .s8
call string$to_stdout
mov rdi, [rbx+url_authinfo_ofs]
call string$to_stdoutln
mov rdi, .s9
call string$to_stdout
mov rdi, [rbx+url_ref_ofs]
call string$to_stdoutln
pop rbx
epilog
cleartext .pref, '::URL debug::'
cleartext .s1, ' protocol: '
cleartext .s2, ' host: '
cleartext .s3, ' port: '
cleartext .s4, ' file: '
cleartext .s5, ' query: '
cleartext .s6, 'authority: '
cleartext .s7, ' path: '
cleartext .s8, ' authinfo: '
cleartext .s9, ' ref: '
end if
if used url$setprotocol | defined include_everything
; two arguments: rdi == url object, rsi == string protocol (we copy it)
falign
url$setprotocol:
prolog url$setprotocol
push rdi rsi
mov rdi, [rdi+url_protocol_ofs]
call heap$free
pop rdi
call string$copy
pop rdi
mov [rdi+url_protocol_ofs], rax
epilog
end if
if used url$sethost | defined include_everything
; two arguments: rdi == url object, rsi == string host (we copy it)
falign
url$sethost:
prolog url$sethost
push rdi rsi
mov rdi, [rdi+url_host_ofs]
call heap$free
pop rdi
call string$copy
pop rdi
mov [rdi+url_host_ofs], rax
epilog
end if
if used url$setport | defined include_everything
; two arguments: rdi == url object, esi == port (integer)
falign
url$setport:
prolog url$setport
mov [rdi+url_port_ofs], esi
epilog
end if
if used url$setfile | defined include_everything
; two arguments: rdi == url object, rsi == string file (we copy it)
falign
url$setfile:
prolog url$setfile
push rdi rsi
mov rdi, [rdi+url_file_ofs]
call heap$free
pop rdi
call string$copy
pop rdi
mov [rdi+url_file_ofs], rax
epilog
end if
if used url$setquery | defined include_everything
; two arguments: rdi == url object, rsi == string query (we copy it)
falign
url$setquery:
prolog url$setquery
push rdi rsi
mov rdi, [rdi+url_query_ofs]
call heap$free
pop rdi
call string$copy
pop rdi
mov [rdi+url_query_ofs], rax
epilog
end if
if used url$setauthority | defined include_everything
; two arguments: rdi == url object, rsi == string authority (we copy it)
falign
url$setauthority:
prolog url$setauthority
push rdi rsi
mov rdi, [rdi+url_authority_ofs]
call heap$free
pop rdi
call string$copy
pop rdi
mov [rdi+url_authority_ofs], rax
epilog
end if
if used url$setpath | defined include_everything
; two arguments: rdi == url object, rsi == string path (we copy it)
falign
url$setpath:
prolog url$setpath
push rdi rsi
mov rdi, [rdi+url_path_ofs]
call heap$free
pop rdi
call string$copy
pop rdi
mov [rdi+url_path_ofs], rax
epilog
end if
if used url$setauthinfo | defined include_everything
; two arguments: rdi == url object, rsi == string authinfo (we copy it)
falign
url$setauthinfo:
prolog url$setauthinfo
push rdi rsi
mov rdi, [rdi+url_authinfo_ofs]
call heap$free
pop rdi
call string$copy
pop rdi
mov [rdi+url_authinfo_ofs], rax
epilog
end if
if used url$setref | defined include_everything
; two arguments: rdi == url object, rsi == string ref (we copy it)
falign
url$setref:
prolog url$setref
push rdi rsi
mov rdi, [rdi+url_ref_ofs]
call heap$free
pop rdi
call string$copy
pop rdi
mov [rdi+url_ref_ofs], rax
epilog
end if
if used url$topreface | defined include_everything
; single argument in rdi: url object
; returns external representation of the url object suitable for an HTTP request preface
falign
url$topreface:
prolog url$topreface
push rbx r12
mov rbx, rdi
call buffer$new
mov r12, rax
mov rsi, [rbx+url_path_ofs]
cmp qword [rsi], 0
je .slashonly
; url encode the path:
mov rdi, rsi
mov esi, 1 ; path encoding
call url$encode
push rax
mov rdi, r12
mov rsi, rax
call buffer$append_rawstring
pop rdi
call heap$free
jmp .checkquery
cleartext .slash, '/'
calign
.slashonly:
mov rdi, r12
mov rsi, .slash
call buffer$append_rawstring
calign
.checkquery:
mov rsi, [rbx+url_query_ofs]
cmp qword [rsi], 0
je .checkref
mov rdi, r12
mov rsi, .questionmark
call buffer$append_rawstring
if defined url_querystring_decode
; url encode the query
mov rdi, [rbx+url_query_ofs]
xor esi, esi
call url$encode
push rax
mov rdi, r12
mov rsi, rax
call buffer$append_rawstring
pop rdi
call heap$free
else
mov rdi, r12
mov rsi, [rbx+url_query_ofs]
call buffer$append_rawstring
end if
calign
.checkref:
mov rsi, [rbx+url_ref_ofs]
cmp qword [rsi], 0
je .alldone
mov rdi, r12
mov rsi, .hash
call buffer$append_rawstring
; url encode the ref (aka fragment)
mov rdi, [rbx+url_ref_ofs]
xor esi, esi
call url$encode
push rax
mov rdi, r12
mov rsi, rax
call buffer$append_rawstring
pop rdi
call heap$free
calign
.alldone:
mov rdi, [r12+buffer_itself_ofs]
mov rsi, [r12+buffer_length_ofs]
if string_bits = 32
call string$from_utf32
else
call string$from_utf16
end if
mov rdi, r12
mov r12, rax
call buffer$destroy
mov rax, r12
pop r12 rbx
epilog
cleartext .questionmark, '?'
cleartext .hash, '#'
end if
if used url$tostring | defined include_everything
; single argument in rdi: url object
; returns external representation of the url object in a new string (note: does not add authinfo)
falign
url$tostring:
prolog url$tostring
push rbx r12
mov rbx, rdi
call buffer$new
mov r12, rax
mov rdi, rax
mov rsi, [rbx+url_protocol_ofs]
call buffer$append_rawstring
mov rsi, [rbx+url_authority_ofs]
cmp qword [rsi], 0
je .skipauthority
mov rdi, r12
mov rsi, .colonslashslash
call buffer$append_rawstring
mov rdi, r12
mov rsi, [rbx+url_authority_ofs]
call buffer$append_rawstring
jmp .checkpath
calign
.skipauthority:
mov rdi, r12
mov rsi, .colon
call buffer$append_rawstring
calign
.checkpath:
mov rsi, [rbx+url_path_ofs]
cmp qword [rsi], 0
je .checkquery
; url encode the path:
mov rdi, rsi
mov esi, 1 ; path encoding
call url$encode
push rax
mov rdi, r12
mov rsi, rax
call buffer$append_rawstring
pop rdi
call heap$free
calign
.checkquery:
mov rsi, [rbx+url_query_ofs]
cmp qword [rsi], 0
je .checkref
mov rdi, r12
mov rsi, .questionmark
call buffer$append_rawstring
if defined url_querystring_decode
; url encode the query
mov rdi, [rbx+url_query_ofs]
xor esi, esi
call url$encode
push rax
mov rdi, r12
mov rsi, rax
call buffer$append_rawstring
pop rdi
call heap$free
else
mov rdi, r12
mov rsi, [rbx+url_query_ofs]
call buffer$append_rawstring
end if
calign
.checkref:
mov rsi, [rbx+url_ref_ofs]
cmp qword [rsi], 0
je .alldone
mov rdi, r12
mov rsi, .hash
call buffer$append_rawstring
; url encode the ref (aka fragment)
mov rdi, [rbx+url_ref_ofs]
xor esi, esi
call url$encode
push rax
mov rdi, r12
mov rsi, rax
call buffer$append_rawstring
pop rdi
call heap$free
calign
.alldone:
mov rdi, [r12+buffer_itself_ofs]
mov rsi, [r12+buffer_length_ofs]
if string_bits = 32
call string$from_utf32
else
call string$from_utf16
end if
mov rdi, r12
mov r12, rax
call buffer$destroy
mov rax, r12
pop r12 rbx
epilog
cleartext .colonslashslash, '://'
cleartext .colon, ':'
cleartext .questionmark, '?'
cleartext .hash, '#'
end if
if used url$encode | defined include_everything
; two arguments: rdi == string containing [possibly] invalid url characters, bool in esi as to whether it is a path or not
; returns a new string in rax with the offenders converted to %XX format
; NOTE: this is not terribly efficient, because it converts to UTF8 and back
falign
url$encode:
prolog url$encode
cmp qword [rdi], 0
je .emptystring
push rbx r12 r13 r14 r15
mov rbx, rdi
mov r15d, esi
call string$utf8_length
mov r12, rax
add rax, 0xf
and rax, not 0xf
sub rsp, rax
mov rdi, rbx
mov rsi, rsp
call string$to_utf8
call buffer$new
mov r13, rax
; determine the buffer length we need
mov r14, r12
mov rdi, rsp
mov rcx, r12
calign
.lengthscan:
mov rdx, r14
movzx eax, byte [rdi]
add rdx, 2
cmp byte [rax*4+.enctable], '%'
cmove r14, rdx
add rdi, 1
sub rcx, 1
jnz .lengthscan
mov rdi, r13
mov rsi, r14
call buffer$reserve
mov rdi, [r13+buffer_itself_ofs]
mov rsi, rsp
mov rcx, r12
calign
.doit:
movzx eax, byte [rsi]
add rsi, 1
mov r8d, [rax*4+.pathenctable]
mov eax, [rax*4+.enctable]
test r15d, r15d
cmovnz eax, r8d
cmp al, '%'
je .doit_perc
mov byte [rdi], al
add rdi, 1
sub rcx, 1
jnz .doit
jmp .done
calign
.doit_perc:
mov [rdi], eax
add rdi, 3
sub rcx, 1
jnz .doit
calign
.done:
mov rsi, rdi
mov rdi, [r13+buffer_itself_ofs]
sub rsi, rdi
call string$from_utf8
mov r14, rax
mov rdi, r13
call buffer$destroy
mov rax, r12
add rax, 0xf
and rax, not 0xf
add rsp, rax
mov rax, r14
pop r15 r14 r13 r12 rbx
epilog
calign
.emptystring:
call string$new
epilog
dalign
.enctable:
db '%00',0,'%01',0,'%02',0,'%03',0,'%04',0,'%05',0,'%06',0,'%07',0
db '%08',0,'%09',0,'%0A',0,'%0B',0,'%0C',0,'%0D',0,'%0E',0,'%0F',0
db '%10',0,'%11',0,'%12',0,'%13',0,'%14',0,'%15',0,'%16',0,'%17',0
db '%18',0,'%19',0,'%1A',0,'%1B',0,'%1C',0,'%1D',0,'%1E',0,'%1F',0
db '+',0,0,0,'!',0,0,0,'%22',0,'%23',0,'$',0,0,0,'%25',0,'%26',0,0x27,0,0,0
db '(',0,0,0,')',0,0,0,'*',0,0,0,'%2B',0,',',0,0,0,'-',0,0,0,'.',0,0,0,'%2F',0
db '0',0,0,0,'1',0,0,0,'2',0,0,0,'3',0,0,0,'4',0,0,0,'5',0,0,0,'6',0,0,0,'7',0,0,0
db '8',0,0,0,'9',0,0,0,'%3A',0,'%3B',0,'%3C',0,'%3D',0,'%3E',0,'%3F',0
db '%40',0,'A',0,0,0,'B',0,0,0,'C',0,0,0,'D',0,0,0,'E',0,0,0,'F',0,0,0,'G',0,0,0
db 'H',0,0,0,'I',0,0,0,'J',0,0,0,'K',0,0,0,'L',0,0,0,'M',0,0,0,'N',0,0,0,'O',0,0,0
db 'P',0,0,0,'Q',0,0,0,'R',0,0,0,'S',0,0,0,'T',0,0,0,'U',0,0,0,'V',0,0,0,'W',0,0,0
db 'X',0,0,0,'Y',0,0,0,'Z',0,0,0,'%5B',0,'%5C',0,'%5D',0,'%5E',0,'_',0,0,0
db '%60',0,'a',0,0,0,'b',0,0,0,'c',0,0,0,'d',0,0,0,'e',0,0,0,'f',0,0,0,'g',0,0,0
db 'h',0,0,0,'i',0,0,0,'j',0,0,0,'k',0,0,0,'l',0,0,0,'m',0,0,0,'n',0,0,0,'o',0,0,0
db 'p',0,0,0,'q',0,0,0,'r',0,0,0,'s',0,0,0,'t',0,0,0,'u',0,0,0,'v',0,0,0,'w',0,0,0
db 'x',0,0,0,'y',0,0,0,'z',0,0,0,'%7B',0,'%7C',0,'%7D',0,'%7E',0,'%7F',0
db '%80',0,'%81',0,'%82',0,'%83',0,'%84',0,'%85',0,'%86',0,'%87',0
db '%88',0,'%89',0,'%8A',0,'%8B',0,'%8C',0,'%8D',0,'%8E',0,'%8F',0
db '%90',0,'%91',0,'%92',0,'%93',0,'%94',0,'%95',0,'%96',0,'%97',0
db '%98',0,'%99',0,'%9A',0,'%9B',0,'%9C',0,'%9D',0,'%9E',0,'%9F',0
db '%A0',0,'%A1',0,'%A2',0,'%A3',0,'%A4',0,'%A5',0,'%A6',0,'%A7',0
db '%A8',0,'%A9',0,'%AA',0,'%AB',0,'%AC',0,'%AD',0,'%AE',0,'%AF',0
db '%B0',0,'%B1',0,'%B2',0,'%B3',0,'%B4',0,'%B5',0,'%B6',0,'%B7',0
db '%B8',0,'%B9',0,'%BA',0,'%BB',0,'%BC',0,'%BD',0,'%BE',0,'%BF',0
db '%C0',0,'%C1',0,'%C2',0,'%C3',0,'%C4',0,'%C5',0,'%C6',0,'%C7',0
db '%C8',0,'%C9',0,'%CA',0,'%CB',0,'%CC',0,'%CD',0,'%CE',0,'%CF',0
db '%D0',0,'%D1',0,'%D2',0,'%D3',0,'%D4',0,'%D5',0,'%D6',0,'%D7',0
db '%D8',0,'%D9',0,'%DA',0,'%DB',0,'%DC',0,'%DD',0,'%DE',0,'%DF',0
db '%E0',0,'%E1',0,'%E2',0,'%E3',0,'%E4',0,'%E5',0,'%E6',0,'%E7',0
db '%E8',0,'%E9',0,'%EA',0,'%EB',0,'%EC',0,'%ED',0,'%EE',0,'%EF',0
db '%F0',0,'%F1',0,'%F2',0,'%F3',0,'%F4',0,'%F5',0,'%F6',0,'%F7',0
db '%F8',0,'%F9',0,'%FA',0,'%FB',0,'%FC',0,'%FD',0,'%FE',0,'%FF'
dalign
.pathenctable:
db '%00',0,'%01',0,'%02',0,'%03',0,'%04',0,'%05',0,'%06',0,'%07',0
db '%08',0,'%09',0,'%0A',0,'%0B',0,'%0C',0,'%0D',0,'%0E',0,'%0F',0
db '%10',0,'%11',0,'%12',0,'%13',0,'%14',0,'%15',0,'%16',0,'%17',0
db '%18',0,'%19',0,'%1A',0,'%1B',0,'%1C',0,'%1D',0,'%1E',0,'%1F',0
db '%20',0,'!',0,0,0,'%22',0,'%23',0,'$',0,0,0,'%25',0,'%26',0,0x27,0,0,0
db '(',0,0,0,')',0,0,0,'*',0,0,0,'+',0,0,0,',',0,0,0,'-',0,0,0,'.',0,0,0,'/',0,0,0
db '0',0,0,0,'1',0,0,0,'2',0,0,0,'3',0,0,0,'4',0,0,0,'5',0,0,0,'6',0,0,0,'7',0,0,0
db '8',0,0,0,'9',0,0,0,'%3A',0,'%3B',0,'%3C',0,'=',0,0,0,'%3E',0,'%3F',0
db '%40',0,'A',0,0,0,'B',0,0,0,'C',0,0,0,'D',0,0,0,'E',0,0,0,'F',0,0,0,'G',0,0,0
db 'H',0,0,0,'I',0,0,0,'J',0,0,0,'K',0,0,0,'L',0,0,0,'M',0,0,0,'N',0,0,0,'O',0,0,0
db 'P',0,0,0,'Q',0,0,0,'R',0,0,0,'S',0,0,0,'T',0,0,0,'U',0,0,0,'V',0,0,0,'W',0,0,0
db 'X',0,0,0,'Y',0,0,0,'Z',0,0,0,'%5B',0,'%5C',0,'%5D',0,'%5E',0,'_',0,0,0
db '%60',0,'a',0,0,0,'b',0,0,0,'c',0,0,0,'d',0,0,0,'e',0,0,0,'f',0,0,0,'g',0,0,0
db 'h',0,0,0,'i',0,0,0,'j',0,0,0,'k',0,0,0,'l',0,0,0,'m',0,0,0,'n',0,0,0,'o',0,0,0
db 'p',0,0,0,'q',0,0,0,'r',0,0,0,'s',0,0,0,'t',0,0,0,'u',0,0,0,'v',0,0,0,'w',0,0,0
db 'x',0,0,0,'y',0,0,0,'z',0,0,0,'%7B',0,'%7C',0,'%7D',0,'%7E',0,'%7F',0
db '%80',0,'%81',0,'%82',0,'%83',0,'%84',0,'%85',0,'%86',0,'%87',0
db '%88',0,'%89',0,'%8A',0,'%8B',0,'%8C',0,'%8D',0,'%8E',0,'%8F',0
db '%90',0,'%91',0,'%92',0,'%93',0,'%94',0,'%95',0,'%96',0,'%97',0
db '%98',0,'%99',0,'%9A',0,'%9B',0,'%9C',0,'%9D',0,'%9E',0,'%9F',0
db '%A0',0,'%A1',0,'%A2',0,'%A3',0,'%A4',0,'%A5',0,'%A6',0,'%A7',0
db '%A8',0,'%A9',0,'%AA',0,'%AB',0,'%AC',0,'%AD',0,'%AE',0,'%AF',0
db '%B0',0,'%B1',0,'%B2',0,'%B3',0,'%B4',0,'%B5',0,'%B6',0,'%B7',0
db '%B8',0,'%B9',0,'%BA',0,'%BB',0,'%BC',0,'%BD',0,'%BE',0,'%BF',0
db '%C0',0,'%C1',0,'%C2',0,'%C3',0,'%C4',0,'%C5',0,'%C6',0,'%C7',0
db '%C8',0,'%C9',0,'%CA',0,'%CB',0,'%CC',0,'%CD',0,'%CE',0,'%CF',0
db '%D0',0,'%D1',0,'%D2',0,'%D3',0,'%D4',0,'%D5',0,'%D6',0,'%D7',0
db '%D8',0,'%D9',0,'%DA',0,'%DB',0,'%DC',0,'%DD',0,'%DE',0,'%DF',0
db '%E0',0,'%E1',0,'%E2',0,'%E3',0,'%E4',0,'%E5',0,'%E6',0,'%E7',0
db '%E8',0,'%E9',0,'%EA',0,'%EB',0,'%EC',0,'%ED',0,'%EE',0,'%EF',0
db '%F0',0,'%F1',0,'%F2',0,'%F3',0,'%F4',0,'%F5',0,'%F6',0,'%F7',0
db '%F8',0,'%F9',0,'%FA',0,'%FB',0,'%FC',0,'%FD',0,'%FE',0,'%FF'
end if
if used url$decode | defined include_everything
; single argument in rdi: string containing %XX url characters
; returns a new string in rax of the decoded goods
falign
url$decode:
prolog url$decode
cmp qword [rdi], 0
je .emptystring
push rbx r12 r13
sub rsp, 32
mov qword [rsp], 4
if string_bits = 32
mov dword [rsp+8], '0'
mov dword [rsp+12], 'x'
else
mov word [rsp+8], '0'
mov word [rsp+10], 'x'
end if
mov rbx, rdi
mov r12, [rdi]
call buffer$new
mov r13, rax
mov rdi, rax
mov rsi, r12
shl rsi, 2
call buffer$reserve
mov rdi, [r13+buffer_itself_ofs]
lea rsi, [rbx+8]
calign
.doit:
if string_bits = 32
mov eax, [rsi]
add rsi, 4
else
movzx eax, word [rsi]
add rsi, 2
end if
cmp eax, '%'
je .doit_perc
cmp eax, '+'
je .doit_plus
mov [rdi], eax
if string_bits = 32
add rdi, 4
else
add rdi, 2
end if
sub r12, 1
jnz .doit
jmp .done
calign
.doit_perc:
sub r12, 1
jz .done
cmp r12, 2
jb .done
if string_bits = 32
mov eax, [rsi]
mov ecx, [rsi+4]
add rsi, 8
mov [rsp+16], eax
mov [rsp+20], ecx
push rdi rsi
lea rdi, [rsp+16]
call string$to_unsigned
pop rsi rdi
mov [rdi], eax
add rdi, 4
else
movzx eax, word [rsi]
movzx ecx, word [rsi+2]
add rsi, 4
mov [rsp+12], ax
mov [rsp+14], cx
push rdi rsi
lea rdi, [rsp+16]
call string$to_unsigned
pop rsi rdi
mov [rdi], eax
add rdi, 2
end if
sub r12, 2
jnz .doit
jmp .done
calign
.doit_plus:
mov dword [rdi], ' '
if string_bits = 32
add rdi, 4
else
add rdi, 2
end if
sub r12, 1
jnz .doit
calign
.done:
mov rsi, rdi
mov rdi, [r13+buffer_itself_ofs]
sub rsi, rdi
if string_bits = 32
call string$from_utf32
else
call string$from_utf16
end if
mov r12, rax
mov rdi, r13
call buffer$destroy
add rsp, 32
mov rax, r12
pop r13 r12 rbx
epilog
calign
.emptystring:
call string$new
epilog
end if
if used url$decode_path | defined include_everything
; single argument in rdi: string containing %XX url characters
; returns a new string in rax of the decoded goods
; NOTE: this is the same as url$decode but leaves + signs alone
falign
url$decode_path:
prolog url$decode_path
cmp qword [rdi], 0
je .emptystring
push rbx r12 r13
sub rsp, 32
mov qword [rsp], 4
if string_bits = 32
mov dword [rsp+8], '0'
mov dword [rsp+12], 'x'
else
mov word [rsp+8], '0'
mov word [rsp+10], 'x'
end if
mov rbx, rdi
mov r12, [rdi]
call buffer$new
mov r13, rax
mov rdi, rax
mov rsi, r12
shl rsi, 2
call buffer$reserve
mov rdi, [r13+buffer_itself_ofs]
lea rsi, [rbx+8]
calign
.doit:
if string_bits = 32
mov eax, [rsi]
add rsi, 4
else
movzx eax, word [rsi]
add rsi, 2
end if
cmp eax, '%'
je .doit_perc
mov [rdi], eax
if string_bits = 32
add rdi, 4
else
add rdi, 2
end if
sub r12, 1
jnz .doit
jmp .done
calign
.doit_perc:
sub r12, 1
jz .done
cmp r12, 2
jb .done
if string_bits = 32
mov eax, [rsi]
mov ecx, [rsi+4]
add rsi, 8
mov [rsp+16], eax
mov [rsp+20], ecx
push rdi rsi
lea rdi, [rsp+16]
call string$to_unsigned
pop rsi rdi
mov [rdi], eax
add rdi, 4
else
movzx eax, word [rsi]
movzx ecx, word [rsi+2]
add rsi, 4
mov [rsp+12], ax
mov [rsp+14], cx
push rdi rsi
lea rdi, [rsp+16]
call string$to_unsigned
pop rsi rdi
mov [rdi], eax
add rdi, 2
end if
sub r12, 2
jnz .doit
jmp .done
calign
.done:
mov rsi, rdi
mov rdi, [r13+buffer_itself_ofs]
sub rsi, rdi
if string_bits = 32
call string$from_utf32
else
call string$from_utf16
end if
mov r12, rax
mov rdi, r13
call buffer$destroy
add rsp, 32
mov rax, r12
pop r13 r12 rbx
epilog
calign
.emptystring:
call string$new
epilog
end if