Introduction
This tutorial (others will follow), provides the dynamic link library code for a set of routines that permit the developer to read and write variable length data to a disc file.
The concept used is some forty years old, and is loosely based upon the ICL 1900 disc housekeeping serial access method. Now, you may well see, if you scrutinize the code, that what I have written is ever so slightly over the top for a set of disc housekeeping routines that permit you to read and write variable length records. Indeed, you would be one hundred percent correct is stating this, but you are seeing a fraction of the finished package.
Buckets
The files that are managed by this set of routines consist of a number of buckets. The term bucket is used to describe a logical block of data held on disc that contains one or more physical disc sectors. Each bucket has a bucket header that contains information pertaining to that bucket, such as the number of bytes used and the number of bytes free. This bucket header is found at the start of each bucket written to the file. When the file is created, the bucket size provided to the CreateSerialFile function fixes the bucket size for that file.
EOF Bucket
There is an end of file bucket which is written to the file when the CloseFile function is called on a file created by the CreateSerialFile function.
Records
Each bucket may contain one or more variable length records. A record must be wholly contained within the bucket (in other words, records cannot span buckets). The record consists of a word header, followed by the actual data for the record.
Writing To A File
The CreateSerialFile function creates a new file for writing. The WriteRecord function is used to write a record to the file, and length of the record being written determines if the record is placed in the current open bucket, or the current bucket is physically written to the disc, and a new bucket created into which that record will be placed.
Reading From A File
The OpenSerialFile function opens an existing file for reading. The ReadRecord function operates in two modes. If the address of the buffer into which the record being read is to be placed is NULL, then the function returns the number of bytes needed to read the current record. If the address of the buffer is not NULL, then it is assumed that it is large enough to contain the record being read.
Handles
Each process may open up to 128 files simultaneously, but as each file is opened exclusively, no other process can open a file that is already opened by a process.
The Code
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; D i s c H o u s e k e e p i n g S e r i a l A c c e s s
;
; Version 1.0, written for dream-in-code April 2011
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.686
.model flat, stdcall
option casemap:none
public _DllMainCRTStartup@12
CREATE_NEW equ 1
OPEN_EXISTING equ 3
FILE_ATTRIBUTE_NORMAL equ 080H
GENERIC_READ equ 080000000H
GENERIC_WRITE equ 040000000H
INVALID_HANDLE_VALUE equ 0FFFFFFFFH
DLL_PROCESS_ATTACH equ 1
DLL_PROCESS_DETACH equ 0
MEM_COMMIT equ 01000H
MEM_RELEASE equ 08000H
PAGE_READWRITE equ 04H
SERIAL_WRITE equ 1
SERIAL_READ equ 2
SERIAL_FILE equ 1
EOF_BUCKET equ 080H
OVERFLOW_BUCKET equ 040H
MAX_HANDLES equ 128
Sleep proto :DWORD
VirtualAlloc proto :DWORD, :DWORD, :DWORD, :DWORD
VirtualFree proto :DWORD, :DWORD, :DWORD
CreateFileA proto :DWORD, :DWORD, :DWORD, \
:DWORD, :DWORD, :DWORD, :DWORD
ReadFile proto :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
WriteFile proto :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
SetFilePointer proto :DWORD, :DWORD, :DWORD, :DWORD
CloseHandle proto :DWORD
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Internal file handle entry - a maximum of 128 handles permitted
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
dfh_handle struc
handle dd ?
bucket_sz dd ?
access_mode dw ?
buffer dd ?
write_bucket dd ?
read_bucket dd ?
current_offset dd ?
dfh_handle ends
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Bucket header written at the start of each bucket
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
header struc
next_bucket dd ?
oflow_bucket dd ?
used_bytes dw ?
free_bytes dw ?
flags db ?
header ends
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.data
handle_table dd 0
handles_open dd 0
handles_sync dd 0
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.code
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Read the next bucket from the file
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ReadBucket proc
local bytes_read
invoke ReadFile, dfh_handle.handle[esi], \
dfh_handle.buffer[esi], \
dfh_handle.bucket_sz[esi], \
ADDR bytes_read, 0
mov edi, dfh_handle.buffer[esi]
mov ecx, dfh_handle.bucket_sz[esi]
mov eax, SIZE header
mov dfh_handle.current_offset[esi], eax
ret
ReadBucket endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Write the next bucket to the file
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
WriteBucket proc
local bytes_written
invoke WriteFile, dfh_handle.handle[esi], \
dfh_handle.buffer[esi], \
dfh_handle.bucket_sz[esi], \
ADDR bytes_written, 0
mov edi, dfh_handle.buffer[esi]
mov ecx, dfh_handle.bucket_sz[esi]
mov ebx, SIZE header
mov header.used_bytes[edi], bx
sub ecx, ebx
mov header.free_bytes[edi], cx
inc dfh_handle.write_bucket[esi]
add edi, SIZE header
xor al, al
rep stosb
ret
WriteBucket endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Get a free table handle entry
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
GetHandle proc
push esi
xor eax, eax
xor edx, edx
inc edx
cmpxchg handles_sync, edx
jz got_handle_sync
invoke Sleep, 0
jmp GetHandle
got_handle_sync: mov ecx, MAX_HANDLES
mov esi, handle_table
xor eax, eax
find_free_handle: mov ebx, dfh_handle.handle[esi]
or ebx, ebx
je found_free_handle
add esi, SIZE dfh_handle
inc eax
loop find_free_handle
stc
pop esi
ret
found_free_handle: clc
pop esi
ret
GetHandle endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Create Serial File for output only
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CreateSerialFile proc file_name:DWORD, bucket_size:DWORD
local disc_handle:DWORD, table_entry:DWORD
mov eax, bucket_size
and eax, 01FFH
or eax, eax
jne CreateSerialFileError
invoke CreateFileA, file_name, \
GENERIC_READ or GENERIC_WRITE, \
0, 0, CREATE_NEW, \
FILE_ATTRIBUTE_NORMAL, 0
cmp eax, INVALID_HANDLE_VALUE
je CreateSerialFileError
mov disc_handle, eax
invoke GetHandle
jc CreateSerialFileError
mov table_entry, eax
mov ebx, handle_table
lea esi, [ebx+eax*8]
inc handles_open
mov eax, disc_handle
mov dfh_handle.handle[esi], eax
mov ax, SERIAL_WRITE
mov dfh_handle.access_mode[esi], ax
mov eax, bucket_size
mov dfh_handle.bucket_sz[esi], eax
xor eax, eax
mov handles_sync, eax
invoke VirtualAlloc, eax, bucket_size,
MEM_COMMIT, \
PAGE_READWRITE
mov dfh_handle.buffer[esi], eax
mov edi, eax
mov eax, bucket_size
mov bx, SIZE header
mov header.used_bytes[edi], bx
sub ax, bx
mov header.free_bytes[edi], ax
mov al, SERIAL_FILE
mov header.flags[edi], al
mov eax, table_entry
inc eax
ret
CreateSerialFileError: xor eax, eax
dec eax
ret
CreateSerialFile endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Open Serial File for input only
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
OpenSerialFile proc file_name:DWORD
local disc_handle:DWORD, table_entry:DWORD, \
file_header:header, bytes_read:DWORD
invoke CreateFileA, file_name, \
GENERIC_READ or GENERIC_WRITE, \
0, 0, OPEN_EXISTING, \
FILE_ATTRIBUTE_NORMAL, 0
cmp eax, INVALID_HANDLE_VALUE
je OpenSerialFileError
mov disc_handle, eax
invoke GetHandle
jc OpenSerialFileError
mov table_entry, eax
mov ebx, handle_table
lea esi, [ebx+eax*8]
inc handles_open
invoke ReadFile, disc_handle, \
ADDR file_header, \
SIZE header, \
ADDR bytes_read, 0
mov eax, disc_handle
mov dfh_handle.handle[esi], eax
mov ax, SERIAL_READ
mov dfh_handle.access_mode[esi], ax
movzx eax, file_header.used_bytes
add ax, file_header.free_bytes
mov dfh_handle.bucket_sz[esi], eax
xor eax, eax
mov handles_sync, eax
invoke VirtualAlloc, eax, \
dfh_handle.bucket_sz[esi],
MEM_COMMIT, \
PAGE_READWRITE
mov dfh_handle.buffer[esi], eax
invoke SetFilePointer, dfh_handle.handle[esi], \
0, 0, 0
invoke ReadBucket
mov eax, table_entry
inc eax
ret
OpenSerialFileError: xor eax, eax
dec eax
ret
OpenSerialFile endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Close a file and free the internal handle entry
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CloseFile proc handle:DWORD
mov eax, handle
dec eax
mov ebx, handle_table
lea esi, [ebx+eax*8]
mov ax, dfh_handle.access_mode[esi]
cmp ax, SERIAL_WRITE
jne free_buffer
invoke WriteBucket
mov edi, dfh_handle.buffer[esi]
mov al, EOF_BUCKET
mov header.flags[edi], al
invoke WriteBucket
free_buffer: invoke VirtualFree, dfh_handle.buffer[esi], \
0, MEM_RELEASE
invoke CloseHandle, dfh_handle.handle[esi]
xor eax, eax
mov dfh_handle.handle[esi], eax
dec handles_open
ret
CloseFile endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Read the next record from the file
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ReadRecord proc handle:DWORD, \
user_record:DWORD, \
record_length:DWORD
read_initialze: mov eax, handle
dec eax
mov ebx, handle_table
lea esi, [ebx+eax*8]
mov edi, dfh_handle.buffer[esi]
add edi, dfh_handle.current_offset[esi]
push edi
mov eax, DWORD PTR [edi]
or eax, eax
jne transfer_record
invoke ReadBucket
mov eax, handle
dec eax
mov ebx, handle_table
lea esi, [ebx+eax*8]
mov edi, dfh_handle.buffer[esi]
mov al, header.flags[edi]
cmp al, EOF_BUCKET
jne read_initialze
xor eax, eax
dec eax
ret
transfer_record: mov esi, record_length
mov DWORD PTR [esi], eax
mov edi, user_record
or edi, edi
jne copy_record
pop edi
xor eax, eax
ret
copy_record: pop esi
add esi, 4
mov edi, user_record
mov ecx, eax
rep movsb
mov ebx, handle_table
mov edx, handle
dec edx
lea esi, [ebx+edx*8]
add eax, 4
add dfh_handle.current_offset[esi], eax
xor eax, eax
ret
ReadRecord endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Write the next record to the file
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
WriteRecord proc handle:DWORD, \
user_record:DWORD, \
record_length:DWORD
local bytes_written:DWORD
transfer_record: mov eax, handle
dec eax
mov ebx, handle_table
lea esi, [ebx+eax*8]
mov edi, dfh_handle.buffer[esi]
movzx eax, header.free_bytes[edi]
mov ecx, record_length
add ecx, 4
cmp eax, ecx
jb write_bucket
sub eax, ecx
mov header.free_bytes[edi], ax
movzx eax, header.used_bytes[edi]
push eax
add eax, ecx
mov header.used_bytes[edi], ax
pop ebx
mov eax, record_length
mov DWORD PTR [edi+ebx], eax
lea edi, 4[edi+ebx]
mov esi, user_record
sub ecx, 4
rep movsb
xor eax, eax
ret
write_bucket: invoke WriteBucket
jmp transfer_record
WriteRecord endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; DLL main entry point
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_DllMainCRTStartup@12 proc instance:DWORD, why:DWORD, na:DWORD
mov eax, why
cmp eax, DLL_PROCESS_ATTACH
je process_attach
cmp eax, DLL_PROCESS_DETACH
je process_detach
ret
process_attach: xor eax, eax
invoke VirtualAlloc, eax, 4096, MEM_COMMIT, \
PAGE_READWRITE
mov handle_table, eax
xor eax, eax
inc eax
ret
process_detach: invoke VirtualFree, handle_table, 0, MEM_RELEASE
ret
_DllMainCRTStartup@12 endp
end
The Module Definition File
LIBRARY EXPORTS CreateSerialFile @1 OpenSerialFile @2 WriteRecord @3 ReadRecord @4 CloseFile @5
This post has been edited by Martyn.Rae: 25 April 2011 - 08:47 AM




MultiQuote


|