mapped.inc
8l_HeavyThing/mapped.inc
	; ------------------------------------------------------------------------
	; HeavyThing x86_64 assembly language library and showcase programs
	; Copyright Â© 2015, 2016 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/>.
	; ------------------------------------------------------------------------
	;       
	; mapped.inc: mmap "object" helper goods
	;

mapped_base_ofs = 0
mapped_size_ofs = 8
mapped_fd_ofs = 16

mapped_size = 24

if used mapped$new_anon | defined include_everything
	; single argument in rdi == initial size
	; returns new mapped object in rax, or null if it fails
falign
mapped$new_anon:
	prolog	mapped$new_anon
	push	rdi
	mov	edi, mapped_size
	call	heap$alloc
	mov	rsi, [rsp]
	mov	rdi, rax
	mov	[rsp], rax
	call	mapped$init_anon
	pop	rax
	cmp	qword [rax+mapped_base_ofs], -1
	je	.kakked
	epilog
calign
.kakked:
	mov	rdi, rax
	call	heap$free
	xor	eax, eax
	epilog
end if


if used mapped$new_cstr | defined include_everything
	; two arguments: rdi == initial size (only if we create it new), rsi == null terminated latin1 filename
	; returns new mapped object in rax, or null if it fails
falign
mapped$new_cstr:
	prolog	mapped$new_cstr
	push	rsi rdi
	mov	edi, mapped_size
	call	heap$alloc
	mov	rsi, [rsp]
	mov	rdx, [rsp+8]
	mov	[rsp], rax
	mov	rdi, rax
	call	mapped$init_cstr
	mov	rax, [rsp]
	add	rsp, 16
	cmp	qword [rax+mapped_fd_ofs], 0
	jl	.kakked
	epilog
calign
.kakked:
	mov	rdi, rax
	call	heap$free
	xor	eax, eax
	epilog

end if

if used mapped$new | defined include_everything
	; two arguments: rdi == initial size (only if we create it new), rsi == string filename
	; returns new mapped object in rax, or null if it fails
falign
mapped$new:
	prolog	mapped$new
	push	rsi rdi
	mov	edi, mapped_size
	call	heap$alloc
	mov	rsi, [rsp]
	mov	rdx, [rsp+8]
	mov	[rsp], rax
	mov	rdi, rax
	call	mapped$init
	mov	rax, [rsp]
	add	rsp, 16
	cmp	qword [rax+mapped_fd_ofs], 0
	jl	.kakked
	epilog
calign
.kakked:
	mov	rdi, rax
	call	heap$free
	xor	eax, eax
	epilog

end if


if used mapped$init_anon | defined include_everything
	; if you don't want to have a separate 24 byte heap allocation done, the $init* functions
	; accept an address to initialize our 3 state variables

	; two arguments: rdi == mapped object, rsi == initial size
	; [mapped_base_ofs] will be -1 on failure
falign
mapped$init_anon:
	prolog	mapped$init_anon
	push	rdi
	mov	qword [rdi+mapped_base_ofs], -1
	mov	[rdi+mapped_size_ofs], rsi
	mov	qword [rdi+mapped_fd_ofs], -1
	mov	eax, syscall_mmap
	xor	edi, edi
	; rsi as our size is valid
	mov	edx, 0x3
	mov	r10d, 0x22
	mov	r8, -1
	xor	r9d, r9d
	syscall
	pop	rdi
	mov	[rdi+mapped_base_ofs], rax
	epilog
end if

if used mapped$init_cstr | defined include_everything
	; three arguments: rdi == mapped object, rsi == initial size, rdx == null terminated latin1 filename
	; [mapped_fd_ofs] will be <0 on failure
falign
mapped$init_cstr:
	prolog	mapped$init_cstr
	push	rdi
	mov	qword [rdi+mapped_base_ofs], -1
	mov	[rdi+mapped_size_ofs], rsi	; save initial size temporarily
	mov	qword [rdi+mapped_fd_ofs], -1
	; attempt to open our file
	mov	eax, syscall_open
	mov	rdi, rdx
	mov	esi, 0x42			; O_RDWR|O_CREAT
	mov	edx, 0x180			; 0600 mode
	syscall
	cmp	eax, 0
	jl	.openfailed
	mov	rcx, [rsp]
	mov	[rcx+mapped_fd_ofs], rax	; save our fd
	mov	rdi, rax			; fd
	xor	esi, esi			; offset
	mov	edx, 2				; SEEK_END
	mov	eax, syscall_lseek
	syscall
	mov	rcx, [rsp]
	cmp	rax, -1
	je	.failed
	test	rax, rax
	jnz	.sizeokay
	mov	rdi, [rcx+mapped_fd_ofs]	; fd
	mov	rsi, [rcx+mapped_size_ofs]	; size
	mov	eax, syscall_ftruncate
	syscall
	mov	rcx, [rsp]
	mov	rax, [rcx+mapped_size_ofs]
calign
.sizeokay:
	; rax is the size of the file, rcx is our mapped object
	mov	[rcx+mapped_size_ofs], rax
	; mmap is next
	xor	edi, edi			; addr
	mov	rsi, rax			; length
	mov	edx, 0x3			; PROT_READ|PROT_WRITE	prot
	mov	r10d, 0x1			; MAP_SHARED	flags
	mov	r8, [rcx+mapped_fd_ofs]		; fd	fd
	xor	r9d, r9d			; offset
	mov	eax, syscall_mmap
	syscall
	mov	rcx, [rsp]
	mov	[rcx+mapped_base_ofs], rax
	cmp	rax, -1
	je	.failed
	pop	rdi
	epilog
calign
.failed:
	; rcx == our mapped object... close our fd, reset its value to -1 and bail
	mov	rdi, [rcx+mapped_fd_ofs]
	mov	qword [rcx+mapped_fd_ofs], -1
	mov	eax, syscall_close
	syscall
	pop	rdi
	epilog
calign
.openfailed:
	pop	rdi
	epilog
end if

if used mapped$init | defined include_everything
	; three arguments: rdi == mapped object, rsi == initial size, rdx == string filename
	; [mapped_fd_ofs] will be <0 on failure
falign
mapped$init:
	prolog	mapped$init
	mov	qword [rdi+mapped_base_ofs], -1
	mov	[rdi+mapped_size_ofs], rsi	; save initial size temporarily
	mov	qword [rdi+mapped_fd_ofs], -1
	sub	rsp, 256
	mov	[rsp], rdi
	mov	[rsp+8], rdx
	mov	rdi, rdx
	call	string$utf8_length
	test	rax, rax
	jz	.badstring
	cmp	rax, 240
	ja	.badstring
	mov	rsi, rsp
	add	rsi, 16
	mov	byte [rsi+rax], 0		; null terminator in advance
	mov	rdi, [rsp+8]
	call	string$to_utf8
	mov	rdi, [rsp]
	mov	rsi, [rdi+mapped_size_ofs]
	mov	rdx, rsp
	add	rdx, 16
	call	mapped$init_cstr
	add	rsp, 256
	epilog
.badstring:
	add	rsp, 256
	epilog

end if


if used mapped$cleanup | defined include_everything
	; single argument in rdi == mapped object
	; munmap, close the fd (if any)
falign
mapped$cleanup:
	prolog	mapped$cleanup
	push	rdi
	mov	rsi, [rdi+mapped_size_ofs]
	mov	rdi, [rdi+mapped_base_ofs]
	mov	eax, syscall_munmap
	syscall
	pop	rsi
	mov	rdi, [rsi+mapped_fd_ofs]
	cmp	rdi, -1
	je	.nofile
	mov	eax, syscall_close
	syscall
	epilog
calign
.nofile:
	epilog
end if


if used mapped$destroy | defined include_everything
	; single argument in rdi == mapped object, we'll munmap, close the fd (if any), and heap$free it
falign
mapped$destroy:
	prolog	mapped$destroy
	push	rdi
	call	mapped$cleanup
	pop	rdi
	call	heap$free
	epilog
end if

if used mapped$syncpart | defined include_everything
 	; four arguments: rdi == mapped object, rsi == offset, rdx == length, ecx == bool for whether to do it async or not
falign
mapped$syncpart:
	prolog	mapped$syncpart
	; address passed to msync must be page_size aligned, and we should also make sure the length is too
	mov	r10, rsi
	and	r10, page_size - 1
	sub	rsi, r10
	add	rdx, r10
	; now align length to the nearest page_size
	add	rdx, page_size - 1
	and	rdx, not (page_size - 1)
	mov	rdi, [rdi+mapped_base_ofs]
	add	rdi, rsi
	mov	rsi, rdx
	mov	r8d, 1				; MS_ASYNC
	mov	r9d, 4				; MS_SYNC
	test	ecx, ecx
	cmovz	edx, r9d
	cmovnz	edx, r8d
	mov	eax, syscall_msync
	syscall
	epilog
end if


if used mapped$sync | defined include_everything
	; two arguments: rdi == mapped object, esi == bool for whether to do it async or not
falign
mapped$sync:
	prolog	mapped$sync
	mov	eax, syscall_msync
	mov	r8d, 1				; MS_ASYNC
	mov	r9d, 4				; MS_SYNC
	test	esi, esi
	cmovz	edx, r9d
	cmovnz	edx, r8d
	mov	rsi, [rdi+mapped_size_ofs]
	mov	rdi, [rdi+mapped_base_ofs]
	syscall
	epilog
end if

if used mapped$grow | defined include_everything
	; two arguments: rdi == mapped object, rsi == AMOUNT to grow by
	; returns the start of the region we just grew in rax, or null if mremap failed
falign
mapped$grow:
	prolog	mapped$grow
	mov	rdx, [rdi+mapped_size_ofs]
	push	rdx rdi		; save our return and our mapped object
	add	rdx, rsi
	mov	rsi, [rdi+mapped_size_ofs]
	mov	[rdi+mapped_size_ofs], rdx	; new size
	mov	rdi, [rdi+mapped_base_ofs]
	mov	eax, syscall_mremap
	mov	r10d, 1		; MREMAP_MAYMOVE
	syscall
	mov	rdi, [rsp]
	mov	[rdi+mapped_base_ofs], rax
	cmp	qword [rdi+mapped_fd_ofs], -1
	je	.nofile
	mov	rsi, [rdi+mapped_size_ofs]
	mov	rdi, [rdi+mapped_fd_ofs]
	mov	eax, syscall_ftruncate
	syscall
.nofile:
	mov	rdi, [rsp]
	cmp	qword [rdi+mapped_base_ofs], -1
	je	.nullret
	mov	rax, [rsp+8]
	add	rax, qword [rdi+mapped_base_ofs]
	add	rsp, 16
	epilog
calign
.nullret:
	xor	eax, eax
	add	rsp, 16
	epilog
end if

if used mapped$growto | defined include_everything
	; two arguments: rdi == mapped object, rsi == new size to grow TO
	; returns the start of the region we just grew in rax, or null if mremap failed
	; NOTE: use the shrink functions instead if you want to shrink it
falign
mapped$growto:
	prolog	mapped$growto
	mov	rdx, [rdi+mapped_size_ofs]
	push	rdx rdi
	mov	[rdi+mapped_size_ofs], rsi	; new size
	xchg	rdx, rsi
	mov	rdi, [rdi+mapped_base_ofs]
	mov	eax, syscall_mremap
	mov	r10d, 1		; MREMAP_MAYMOVE
	syscall
	mov	rdi, [rsp]
	mov	[rdi+mapped_base_ofs], rax
	cmp	qword [rdi+mapped_fd_ofs], -1
	je	.nofile
	mov	rsi, [rdi+mapped_size_ofs]
	mov	rdi, [rdi+mapped_fd_ofs]
	mov	eax, syscall_ftruncate
	syscall
.nofile:
	mov	rdi, [rsp]
	cmp	qword [rdi+mapped_base_ofs], -1
	je	.nullret
	mov	rax, [rsp+8]
	add	rax, qword [rdi+mapped_base_ofs]
	add	rsp, 16
	epilog
calign
.nullret:
	xor	eax, eax
	add	rsp, 16
	epilog
end if


if used mapped$shrink | defined include_everything
	; two arguments: rdi == mapped object, rsi == AMOUNT to shrink by
falign
mapped$shrink:
	prolog	mapped$shrink
	mov	rdx, [rdi+mapped_size_ofs]
	push	rdi		; save our mapped object
	sub	rdx, rsi
	mov	rsi, [rdi+mapped_size_ofs]
	mov	[rdi+mapped_size_ofs], rdx	; new size
	mov	rdi, [rdi+mapped_base_ofs]
	mov	eax, syscall_mremap
	mov	r10d, 1		; MREMAP_MAYMOVE
	syscall
	mov	rdi, [rsp]
	mov	[rdi+mapped_base_ofs], rax
	cmp	qword [rdi+mapped_fd_ofs], -1
	je	.nofile
	mov	rsi, [rdi+mapped_size_ofs]
	mov	rdi, [rdi+mapped_fd_ofs]
	mov	eax, syscall_ftruncate
	syscall
.nofile:
	pop	rdi
	epilog
end if

if used mapped$shrinkto | defined include_everything
	; two arguments: rdi == mapped object, rsi == new size to shrink TO
falign
mapped$shrinkto:
	prolog	mapped$shrinkto
	mov	rdx, [rdi+mapped_size_ofs]
	push	rdi
	mov	[rdi+mapped_size_ofs], rsi	; new size
	xchg	rdx, rsi
	mov	rdi, [rdi+mapped_base_ofs]
	mov	eax, syscall_mremap
	mov	r10d, 1		; MREMAP_MAYMOVE
	syscall
	mov	rdi, [rsp]
	mov	[rdi+mapped_base_ofs], rax
	cmp	qword [rdi+mapped_fd_ofs], -1
	je	.nofile
	mov	rsi, [rdi+mapped_size_ofs]
	mov	rdi, [rdi+mapped_fd_ofs]
	mov	eax, syscall_ftruncate
	syscall
.nofile:
	pop	rdi
	epilog
end if
