Added sparse file support

This commit is contained in:
Eduard Urbach 2025-05-23 19:35:52 +02:00
parent 2e00b28016
commit dc315b8076
Signed by: eduard
GPG key ID: 49226B848C78F6C8
10 changed files with 32 additions and 37 deletions

View file

@ -1,7 +1,6 @@
package compiler package compiler
import ( import (
"bufio"
"io" "io"
"git.urbach.dev/cli/q/src/config" "git.urbach.dev/cli/q/src/config"
@ -13,24 +12,20 @@ import (
) )
// Write writes the executable to the given writer. // Write writes the executable to the given writer.
func (r *Result) Write(writer io.Writer) error { func (r *Result) Write(writer io.WriteSeeker) {
return write(writer, r.Code, r.Data, r.DLLs) write(writer, r.Code, r.Data, r.DLLs)
} }
// write writes an executable file to the given writer. // write writes an executable file to the given writer.
func write(writer io.Writer, code []byte, data []byte, dlls dll.List) error { func write(writer io.WriteSeeker, code []byte, data []byte, dlls dll.List) {
buffer := bufio.NewWriter(writer)
switch config.TargetOS { switch config.TargetOS {
case config.Linux: case config.Linux:
elf.Write(buffer, code, data) elf.Write(writer, code, data)
case config.Mac: case config.Mac:
macho.Write(buffer, code, data) macho.Write(writer, code, data)
case config.Web: case config.Web:
wasm.Write(buffer, code, data) wasm.Write(writer, code, data)
case config.Windows: case config.Windows:
pe.Write(buffer, code, data, dlls) pe.Write(writer, code, data, dlls)
} }
return buffer.Flush()
} }

View file

@ -10,13 +10,7 @@ func (r *Result) WriteFile(path string) error {
return err return err
} }
err = r.Write(file) r.Write(file)
if err != nil {
file.Close()
return err
}
err = file.Close() err = file.Close()
if err != nil { if err != nil {

View file

@ -1,7 +1,6 @@
package elf package elf
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"io" "io"
@ -21,7 +20,7 @@ type ELF struct {
} }
// Write writes the ELF64 format to the given writer. // Write writes the ELF64 format to the given writer.
func Write(writer io.Writer, codeBytes []byte, dataBytes []byte) { func Write(writer io.WriteSeeker, codeBytes []byte, dataBytes []byte) {
sections := exe.MakeSections(HeaderEnd, codeBytes, dataBytes) sections := exe.MakeSections(HeaderEnd, codeBytes, dataBytes)
code := sections[0] code := sections[0]
data := sections[1] data := sections[1]
@ -76,9 +75,9 @@ func Write(writer io.Writer, codeBytes []byte, dataBytes []byte) {
binary.Write(writer, binary.LittleEndian, &elf.Header) binary.Write(writer, binary.LittleEndian, &elf.Header)
binary.Write(writer, binary.LittleEndian, &elf.CodeHeader) binary.Write(writer, binary.LittleEndian, &elf.CodeHeader)
binary.Write(writer, binary.LittleEndian, &elf.DataHeader) binary.Write(writer, binary.LittleEndian, &elf.DataHeader)
writer.Write(bytes.Repeat([]byte{0x00}, code.Padding)) writer.Seek(int64(code.Padding), io.SeekCurrent)
writer.Write(code.Bytes) writer.Write(code.Bytes)
writer.Write(bytes.Repeat([]byte{0x00}, data.Padding)) writer.Seek(int64(data.Padding), io.SeekCurrent)
writer.Write(data.Bytes) writer.Write(data.Bytes)
if config.Sections { if config.Sections {

View file

@ -1,12 +1,12 @@
package elf_test package elf_test
import ( import (
"io"
"testing" "testing"
"git.urbach.dev/cli/q/src/elf" "git.urbach.dev/cli/q/src/elf"
"git.urbach.dev/cli/q/src/test"
) )
func TestWrite(t *testing.T) { func TestWrite(t *testing.T) {
elf.Write(io.Discard, nil, nil) elf.Write(&test.Discard{}, nil, nil)
} }

View file

@ -1,7 +1,6 @@
package macho package macho
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"io" "io"
@ -24,7 +23,7 @@ type MachO struct {
} }
// Write writes the Mach-O format to the given writer. // Write writes the Mach-O format to the given writer.
func Write(writer io.Writer, codeBytes []byte, dataBytes []byte) { func Write(writer io.WriteSeeker, codeBytes []byte, dataBytes []byte) {
sections := exe.MakeSections(HeaderEnd, codeBytes, dataBytes) sections := exe.MakeSections(HeaderEnd, codeBytes, dataBytes)
code := sections[0] code := sections[0]
data := sections[1] data := sections[1]
@ -116,8 +115,8 @@ func Write(writer io.Writer, codeBytes []byte, dataBytes []byte) {
binary.Write(writer, binary.LittleEndian, &m.CodeHeader) binary.Write(writer, binary.LittleEndian, &m.CodeHeader)
binary.Write(writer, binary.LittleEndian, &m.DataHeader) binary.Write(writer, binary.LittleEndian, &m.DataHeader)
binary.Write(writer, binary.LittleEndian, &m.UnixThread) binary.Write(writer, binary.LittleEndian, &m.UnixThread)
writer.Write(bytes.Repeat([]byte{0x00}, code.Padding)) writer.Seek(int64(code.Padding), io.SeekCurrent)
writer.Write(code.Bytes) writer.Write(code.Bytes)
writer.Write(bytes.Repeat([]byte{0x00}, data.Padding)) writer.Seek(int64(data.Padding), io.SeekCurrent)
writer.Write(data.Bytes) writer.Write(data.Bytes)
} }

View file

@ -1,12 +1,12 @@
package macho_test package macho_test
import ( import (
"io"
"testing" "testing"
"git.urbach.dev/cli/q/src/macho" "git.urbach.dev/cli/q/src/macho"
"git.urbach.dev/cli/q/src/test"
) )
func TestWrite(t *testing.T) { func TestWrite(t *testing.T) {
macho.Write(io.Discard, nil, nil) macho.Write(&test.Discard{}, nil, nil)
} }

View file

@ -24,7 +24,7 @@ type EXE struct {
} }
// Write writes the EXE file to the given writer. // Write writes the EXE file to the given writer.
func Write(writer io.Writer, codeBytes []byte, dataBytes []byte, dlls dll.List) { func Write(writer io.WriteSeeker, codeBytes []byte, dataBytes []byte, dlls dll.List) {
var ( var (
sections = exe.MakeSections(HeaderEnd, codeBytes, dataBytes, nil) sections = exe.MakeSections(HeaderEnd, codeBytes, dataBytes, nil)
code = sections[0] code = sections[0]
@ -144,10 +144,10 @@ func Write(writer io.Writer, codeBytes []byte, dataBytes []byte, dlls dll.List)
binary.Write(writer, binary.LittleEndian, &pe.NTHeader) binary.Write(writer, binary.LittleEndian, &pe.NTHeader)
binary.Write(writer, binary.LittleEndian, &pe.OptionalHeader64) binary.Write(writer, binary.LittleEndian, &pe.OptionalHeader64)
binary.Write(writer, binary.LittleEndian, &pe.Sections) binary.Write(writer, binary.LittleEndian, &pe.Sections)
writer.Write(bytes.Repeat([]byte{0x00}, code.Padding)) writer.Seek(int64(code.Padding), io.SeekCurrent)
writer.Write(code.Bytes) writer.Write(code.Bytes)
writer.Write(bytes.Repeat([]byte{0x00}, data.Padding)) writer.Seek(int64(data.Padding), io.SeekCurrent)
writer.Write(data.Bytes) writer.Write(data.Bytes)
writer.Write(bytes.Repeat([]byte{0x00}, imports.Padding)) writer.Seek(int64(imports.Padding), io.SeekCurrent)
writer.Write(imports.Bytes) writer.Write(imports.Bytes)
} }

View file

@ -1,12 +1,12 @@
package pe_test package pe_test
import ( import (
"io"
"testing" "testing"
"git.urbach.dev/cli/q/src/pe" "git.urbach.dev/cli/q/src/pe"
"git.urbach.dev/cli/q/src/test"
) )
func TestWrite(t *testing.T) { func TestWrite(t *testing.T) {
pe.Write(io.Discard, nil, nil, nil) pe.Write(&test.Discard{}, nil, nil, nil)
} }

View file

@ -26,6 +26,7 @@
- [scope](scope) - Defines a `Scope` used for code blocks - [scope](scope) - Defines a `Scope` used for code blocks
- [set](set) - Generic set implementation - [set](set) - Generic set implementation
- [sizeof](sizeof) - Calculates the byte size of numbers - [sizeof](sizeof) - Calculates the byte size of numbers
- [test](test) - Testing utilities
- [token](token) - Converts a file to tokens with the `Tokenize` function - [token](token) - Converts a file to tokens with the `Tokenize` function
- [types](types) - Type system - [types](types) - Type system
- [x86](x86) - x86-64 implementation - [x86](x86) - x86-64 implementation

7
src/test/Discard.go Normal file
View file

@ -0,0 +1,7 @@
package test
// Discard implements a no-op WriteSeeker.
type Discard struct{}
func (w *Discard) Write(_ []byte) (int, error) { return 0, nil }
func (w *Discard) Seek(_ int64, _ int) (int64, error) { return 0, nil }