diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-01-03 02:19:57 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-01-03 02:19:57 +0800 |
| commit | 210025560c80b48af4875af199f183ad709e3bf0 (patch) | |
| tree | eb11aeb7b641187f207e3b572c5184cf3526b463 /include | |
| parent | bde87490d61135dcaa94d9b1489ff6c39876d169 (diff) | |
| parent | 10614e04929a6d109a5e90524e446b637aaf0c13 (diff) | |
| download | lume-210025560c80b48af4875af199f183ad709e3bf0.tar.gz lume-210025560c80b48af4875af199f183ad709e3bf0.tar.xz | |
Merge pull request #4 from Richard-Qin-001/dev/shell
Rebuilt the system's underlying structure; it is now stable enough to merge.
The system now supports a read-only file system and has a simple shell implemented.
Diffstat (limited to 'include')
39 files changed, 3317 insertions, 106 deletions
diff --git a/include/common/fcntl.h b/include/common/fcntl.h new file mode 100644 index 0000000..24d40fa --- /dev/null +++ b/include/common/fcntl.h @@ -0,0 +1,9 @@ +#pragma once + +#define O_RDONLY 0x000 +#define O_WRONLY 0x001 +#define O_RDWR 0x002 +#define O_CREATE 0x200 +#define O_TRUNC 0x400 + +#define NOFILE 16
\ No newline at end of file diff --git a/include/common/types.h b/include/common/types.h index 7403a54..8924699 100644 --- a/include/common/types.h +++ b/include/common/types.h @@ -1,22 +1,27 @@ #pragma once -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; -typedef signed char int8_t; -typedef signed short int16_t; -typedef signed int int32_t; -typedef signed long long int64_t; +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; -typedef uint64_t size_t; -typedef uint64_t uintptr_t; - -typedef uint64_t uint64; -typedef uint32_t uint32; -typedef uint8_t uint8; +typedef uint64_t size_t; +typedef uint64_t uintptr_t; +typedef uint64_t uint64; +typedef uint32_t uint32; +typedef uint16_t uint16; +typedef uint8_t uint8; typedef unsigned int uint; typedef unsigned short ushort; -typedef unsigned char uchar;
\ No newline at end of file +typedef unsigned char uchar; + +typedef int64_t int64; +typedef int32_t int32; +typedef int16_t int16; +typedef int8_t int8;
\ No newline at end of file diff --git a/include/drivers/plic.h b/include/drivers/plic.h new file mode 100644 index 0000000..11e8c8a --- /dev/null +++ b/include/drivers/plic.h @@ -0,0 +1,17 @@ +#pragma once +#include "common/types.h" + +namespace PLIC +{ + // Global initialization (executed only once by hart 0) + void init(); + + // Per-core initialization (executed when each CPU starts) + void inithart(); + + // Claim interrupt: Get the currently occurring interrupt number (IRQ) + int claim(); + + // End of interrupt: notify the PLIC that this IRQ has been handled + void complete(int irq); +} // namespace PILC diff --git a/include/drivers/uart.h b/include/drivers/uart.h index 4d00d94..26c2394 100644 --- a/include/drivers/uart.h +++ b/include/drivers/uart.h @@ -1,9 +1,36 @@ #pragma once -typedef unsigned long long uint64_t; +#include "common/types.h" -namespace Drivers { +#define UART0 0x10000000L +#define RHR 0 // Receive Holding Register (read mode) +#define THR 0 // Transmit Holding Register (write mode) +#define IER 1 // Interrupt Enable Register +#define FCR 2 // FIFO Control Register +#define ISR 2 // Interrupt Status Register +#define LCR 3 // Line Control Register +#define LSR 5 // Line Status Register + +#define LSR_RX_READY 0x01 // input is waiting to be read from RHR +#define LSR_TX_IDLE 0x20 // THR can accept another character to send + +#define UART_RX_BUF_SIZE 128 + +// ANSI Escape Codes for Modern Terminal Colors +#define ANSI_RESET "\033[0m" +#define ANSI_RED "\033[31m" +#define ANSI_GREEN "\033[32m" +#define ANSI_YELLOW "\033[33m" +#define ANSI_BLUE "\033[34m" +#define ANSI_CLEAR "\033[2J\033[1;1H" + +namespace Drivers +{ void uart_init(); + void uart_intr(); + int console_read(uint64 target, int len); + void uart_putc(char c); - void uart_puts(const char* s); - void print_hex(uint64_t val); + void uart_puts(const char *s); + void uart_put_int(int value); + void print_hex(uint64 value); }
\ No newline at end of file diff --git a/include/drivers/virtio.h b/include/drivers/virtio.h new file mode 100644 index 0000000..ea2c9a6 --- /dev/null +++ b/include/drivers/virtio.h @@ -0,0 +1,103 @@ +#pragma once +#include "common/types.h" + +// VirtIO MMIO Base Address +#define VIRTIO0 0x10001000 + +// MMIO Registers (Modern / Version 2) +#define VIRTIO_MMIO_MAGIC_VALUE 0x000 +#define VIRTIO_MMIO_VERSION 0x004 +#define VIRTIO_MMIO_DEVICE_ID 0x008 +#define VIRTIO_MMIO_VENDOR_ID 0x00c +#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 +#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 +#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 +#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 +#define VIRTIO_MMIO_QUEUE_SEL 0x030 +#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 +#define VIRTIO_MMIO_QUEUE_NUM 0x038 +#define VIRTIO_MMIO_QUEUE_READY 0x044 +#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define VIRTIO_MMIO_STATUS 0x070 + +// 64-bit Queue Address Registers (Modern) +#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 +#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 +#define VIRTIO_MMIO_QUEUE_DRIVER_LOW 0x090 +#define VIRTIO_MMIO_QUEUE_DRIVER_HIGH 0x094 +#define VIRTIO_MMIO_QUEUE_DEVICE_LOW 0x0a0 +#define VIRTIO_MMIO_QUEUE_DEVICE_HIGH 0x0a4 + +// Status Bits +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +#define VIRTIO_CONFIG_S_DRIVER 2 +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +#define VIRTIO_CONFIG_S_FEATURES_OK 8 + +// Feature Bits +#define VIRTIO_BLK_F_RO 5 +#define VIRTIO_BLK_F_SCSI 7 +#define VIRTIO_BLK_F_CONFIG_WCE 11 +#define VIRTIO_BLK_F_MQ 12 +#define VIRTIO_F_ANY_LAYOUT 27 +#define VIRTIO_RING_F_INDIRECT_DESC 28 +#define VIRTIO_RING_F_EVENT_IDX 29 +#define VIRTIO_F_VERSION_1 32 + +// Descriptor Flags +#define VRING_DESC_F_NEXT 1 +#define VRING_DESC_F_WRITE 2 + +#define NUM_DESCS 8 + +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 + +// Device Configuration Layout (Offset 0x100) +#define VIRTIO_MMIO_CONFIG 0x100 + +namespace VirtIO +{ + struct VRingDesc + { + uint64 addr; + uint32 len; + uint16 flags; + uint16 next; + }; + + struct VRingAvail + { + uint16 flags; + uint16 idx; + uint16 ring[NUM_DESCS]; + }; + + struct VRingUsedElem + { + uint32 id; + uint32 len; + }; + + struct VRingUsed + { + uint16 flags; + uint16 idx; + struct VRingUsedElem ring[NUM_DESCS]; + }; + + struct __attribute__((packed)) BlockRequest + { + uint32 type; // 0=Read, 1=Write + uint32 reserved; + uint64 sector; + }; + + void init(); + void intr(); + void rw(int write, uint64 blockno, char *buf); + void test_rw(); + void test_bio(); +}
\ No newline at end of file diff --git a/include/fs/fat32.h b/include/fs/fat32.h new file mode 100644 index 0000000..c794bd7 --- /dev/null +++ b/include/fs/fat32.h @@ -0,0 +1,111 @@ +#pragma once +#include "fs/fs.h" +#include "kernel/buf.h" + +// Disk Layout Structures (On-Disk) + +// FAT32 Boot Sector (BPB) +struct FAT32_BPB +{ + uint8 jmpBoot[3]; + char oemName[8]; + uint16 bytesPerSector; + uint8 secPerClus; + uint16 reservedSecCnt; + uint8 numFATs; + uint16 rootEntCnt; + uint16 totSec16; + uint8 media; + uint16 FATSz16; + uint16 secPerTrk; + uint16 numHeads; + uint32 hiddSec; + uint32 totSec32; + uint32 FATSz32; + uint16 extFlags; + uint16 FSVer; + uint32 rootClus; + uint16 FSInfo; + uint16 bkBootSec; + uint8 reserved[12]; + uint8 drvNum; + uint8 reserved1; + uint8 bootSig; + uint32 volID; + char volLab[11]; + char filSysType[8]; +} __attribute__((packed)); + +// FAT32 Directory Entry (32 bytes) +struct FAT32_DirEnt +{ + char name[11]; // 8.3 filename + uint8 attr; // File attributes + uint8 ntRes; + uint8 crtTimeTenth; + uint16 crtTime; + uint16 crtDate; + uint16 lstAccDate; + uint16 fstClusHI; // High 16 bits of first cluster + uint16 wrtTime; + uint16 wrtDate; + uint16 fstClusLO; // Low 16 bits of first cluster + uint32 fileSize; +} __attribute__((packed)); + +#define ATTR_READ_ONLY 0x01 +#define ATTR_HIDDEN 0x02 +#define ATTR_SYSTEM 0x04 +#define ATTR_VOLUME_ID 0x08 +#define ATTR_DIRECTORY 0x10 +#define ATTR_ARCHIVE 0x20 +#define ATTR_LONG_NAME 0x0F + +// In-Memory Classes + +class FAT32FileSystem; + +class FAT32Inode : public Inode +{ +public: + FAT32FileSystem *fs; + + // FAT32 Private Data + uint32 first_clus; // The starting cluster number of the file + uint32 parent_clus; // Starting cluster number of the parent directory + uint32 dir_offset; // The byte offset of this directory entry in the parent directory + + FAT32Inode() : fs(nullptr), first_clus(0) {} + // Implement VFS Interface + void update() override; + int read(char *dst, uint32 off, uint32 n, int isUser) override; + int write(const char *src, uint32 off, uint32 n, int isUser) override; + Inode *lookup(const char *name) override; + Inode *create(const char *name, short type, short major, short minor) override; + void truncate() override; +}; + +class FAT32FileSystem : public FileSystem +{ +public: + uint32 fat_start_sector; + uint32 data_start_sector; + uint32 root_cluster; + uint32 sectors_per_cluster; + uint32 bytes_per_cluster; + + void init() override; + Inode* root() override; + + // Helper functions + uint32 next_cluster(uint32 cluster); // Check the FAT table for the next cluster + uint32 alloc_cluster(); // Assign a new cluster + uint32 cluster_to_sector(uint32 cluster); + + // directory entry parsing + void read_dir_ent(struct FAT32_DirEnt *de, char *filename); +}; + +extern FAT32FileSystem fat32_fs; + +void fat32_test();
\ No newline at end of file diff --git a/include/fs/file.h b/include/fs/file.h new file mode 100644 index 0000000..2f23176 --- /dev/null +++ b/include/fs/file.h @@ -0,0 +1,36 @@ +#pragma once +#include "common/types.h" +#include "fs/fs.h" + +enum FileType +{ + FD_NONE, + FD_INODE, + FD_DEVICE, + FD_PIPE +}; + +struct file +{ + enum FileType type; + int ref; + char readable; + char writable; + + struct pipe *pipe; // pipeline pointer + Inode *ip; // VFS Inode + uint32 off; // Current Offset +}; + +namespace FileTable +{ + void init(); + struct file *alloc(); // Allocate a free file handle + struct file *dup(struct file *f); // Duplicate handle (sys_dup) + void close(struct file *f); // Close handle + + int stat(struct file *f, uint64 addr); + int read(struct file *f, uint64 addr, int n); + int write(struct file *f, uint64 addr, int n); + int lseek(struct file *f, int offset, int whence); +} // namespace FileTable diff --git a/include/fs/fs.h b/include/fs/fs.h new file mode 100644 index 0000000..f40bc0c --- /dev/null +++ b/include/fs/fs.h @@ -0,0 +1,93 @@ +#pragma once +#include "common/types.h" +#include "kernel/spinlock.h" +#include "fs/stat.h" + +class FileSystem; + +// VFS Inode (Abstract Base Class) + +class Inode +{ +private: + ; +public: + // VFS Common Fields + uint32 dev; // Device number (VirtIO Block ID) + uint32 inum; // Inode number + int ref_cnt; // Memory reference count (number of struct file or processes referencing it) + int valid; // Whether the data is valid (whether metadata has been read from disk) + + Mutex lock; // Sleep lock: protects internal Inode data and atomicity of read/write operations + + // Metadata Cache (for speeding up getattr) + short type; // T_DIR, T_FILE, T_DEVICE + short major; // Major device number (only for T_DEVICE) + short minor; // Minor device number (only for T_DEVICE) + short nlink; // Number of hard links + uint32 size; // File size + + Inode() : ref_cnt(0), valid(0) + { + lock.init("inode"); + } + virtual ~Inode() {} + + /* Core Operation Interface (Subclass Must Implement) */ + // Update metadata + // Sync size, type, links, etc. from memory back to disk + virtual void update() = 0; + // Read content + // Read n bytes from the file at offset into dst + // Returns the actual number of bytes read, returns -1 on error + virtual int read(char *dst, uint32 off, uint32 n, int isUser) = 0; + // 3. Write content + // Write n bytes from src to the file at the given offset + // Need to handle file growth + virtual int write(const char *src, uint32 off, uint32 n, int isUser) = 0; + // Directory Lookup (only valid when type == T_DIR) + // Search for a file named 'name' in the current directory and return its Inode pointer (locked) + // Return nullptr if not found + virtual Inode *lookup(const char *name) = 0; + // Create file/directory (only effective when type == T_DIR) + // Create a new entry in the current directory + virtual Inode *create(const char *name, short type, short major, short minor) = 0; + // Get file status (populate the stat structure) + // Default implementation, subclasses can override to provide more detailed information + virtual void getattr(struct kstat *st); + // Truncate file (set size to 0, release all data blocks) + virtual void truncate() = 0; +}; + +// File System Abstraction (SuperBlock) +class FileSystem +{ +private: + ; +public: + virtual void init() = 0; + virtual Inode* root() = 0; + virtual ~FileSystem() {}; +}; + +// Inode Cache Interface (Global Function) +namespace VFS +{ + void init(); + + // Decrease the reference count, and if it reaches 0, free the memory + void iput(Inode *ip); + // Increase reference count + Inode *idup(Inode *ip); + // lock and load data + void ilock(Inode *ip); + // unlock + void iunlock(Inode *ip); + void iunlockput(Inode *ip); + // Mount the root filesystem + void mount_root(Inode *root); + // Get system root node + Inode *get_root(); + // Path Analysis + Inode *namei(const char *path); +} // namespace VFS diff --git a/include/fs/stat.h b/include/fs/stat.h new file mode 100644 index 0000000..cdce05a --- /dev/null +++ b/include/fs/stat.h @@ -0,0 +1,22 @@ +#pragma once +#include "common/types.h" + +#define T_DIR 1 // Directory +#define T_FILE 2 // File +#define T_DEVICE 3 // Device + +struct kstat +{ + int dev; // File System's disk device + uint32 ino; // Inode number + short type; //Type of file + short nlink; // Number of links to file + uint64 size; // Size of file in bytes + + uint32 mode; // Protection: rwx + uint32 uid; // User ID + uint32 gid; // Group ID + uint32 atime; // Access time + uint32 mtime; // Modified time + uint32 ctime; // Change time +}; diff --git a/include/kernel/buf.h b/include/kernel/buf.h new file mode 100644 index 0000000..099523d --- /dev/null +++ b/include/kernel/buf.h @@ -0,0 +1,30 @@ +#pragma once +#include "common/types.h" +#include "kernel/spinlock.h" + +#define BSIZE 1024 +#define NBUF 30 + +struct Buf +{ + int valid; // Whether the data has been read from the disk (1=valid, 0=invalid) + int disk; // Whether it has been held by the driver (dirty bit/write-back flag is usually extended here) + uint32 dev; // Device Number + uint32 blockno; // Block number + uint32 refcnt; // Reference Counting + struct Buf *prev; // LRU linked list + struct Buf *next; + uchar data[BSIZE]; + + Mutex lock; // Sleep Lock +}; + +namespace BufferCache +{ + void init(); + struct Buf *bread(uint32 dev, uint32 blockno); + void bwrite(struct Buf *b); + void brelse(struct Buf *b); + void bpin(struct Buf *b); + void bunpin(struct Buf *b); +} // namespace BufferCache diff --git a/include/kernel/cpu.h b/include/kernel/cpu.h new file mode 100644 index 0000000..92890b6 --- /dev/null +++ b/include/kernel/cpu.h @@ -0,0 +1,19 @@ +#pragma once +#include "common/types.h" +#include "kernel/proc_def.h" + +struct RunQueue; + +struct cpu +{ + struct Proc *proc; + struct Context context; + int noff; + int intena; + + struct RunQueue *runqueue; +}; + + +extern struct cpu cpus[]; +struct cpu *mycpu();
\ No newline at end of file diff --git a/include/kernel/elf.h b/include/kernel/elf.h new file mode 100644 index 0000000..c216f1c --- /dev/null +++ b/include/kernel/elf.h @@ -0,0 +1,46 @@ +#pragma once +#include "common/types.h" + +#define ELF_MAGIC 0x464C457F // "\x7FELF" in little endian + +// ELF File Header +struct elfhdr +{ + uint32 magic; // Must equal ELF_MAGIC + uint8 elf[12]; + uint16 type; + uint16 machine; + uint32 version; + uint64 entry; // Program Entry Point (Virtual Address) + uint64 phoff; // File offset of the Program Header Table + uint64 shoff; + uint32 flags; + uint16 ehsize; + uint16 phentsize; + uint16 phnum; // Number of Program Headers + uint16 shentsize; + uint16 shnum; + uint16 shstrndx; +}; + +// Program Header +// Describes how to load segments from a file into memory +struct proghdr +{ + uint32 type; + uint32 flags; + uint64 off; // Offset of the section in the file + uint64 vaddr; // Virtual address of the segment in memory + uint64 paddr; + uint64 filesz; // Size of the section in the file + uint64 memsz; // The size of the segment in memory (memsz >= filesz) + uint64 align; +}; + +// Values for Proghdr type +#define ELF_PROG_LOAD 1 + +// Flag bits for Proghdr flags +#define ELF_PROG_FLAG_EXEC 1 +#define ELF_PROG_FLAG_WRITE 2 +#define ELF_PROG_FLAG_READ 4
\ No newline at end of file diff --git a/include/kernel/exec.h b/include/kernel/exec.h new file mode 100644 index 0000000..964e3bb --- /dev/null +++ b/include/kernel/exec.h @@ -0,0 +1,6 @@ +#pragma once + +namespace Exec +{ + int exec(char *path, char **argv); +}
\ No newline at end of file diff --git a/include/kernel/mm.h b/include/kernel/mm.h index 07d675b..3b59edb 100644 --- a/include/kernel/mm.h +++ b/include/kernel/mm.h @@ -3,20 +3,25 @@ namespace VM { - typedef uint64_t *pagetable_t; - typedef uint64_t pte_t; + extern uint64 *kernel_pagetable; - pte_t *walk(pagetable_t pagetable, uint64 va, int alloc); - int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm); - void kvmmap(uint64 va, uint64 pa, uint64 sz, int perm); void kvminit(); void kvminithart(); - pagetable_t uvmcreate(); - void uvminit(pagetable_t pagetable, uchar *src, uint sz); - void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free); - void uvmfree(pagetable_t pagetable, uint64 sz); - int uvmcopy(pagetable_t oldpt, pagetable_t newpt, uint64 sz); - uint64 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz); - uint64 uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz); - int copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len); + + uint64 *uvmcreate(); + void uvminit(uint64 *pagetable, uchar *src, uint64 sz); + uint64 uvmalloc(uint64 *pagetable, uint64 oldsz, uint64 newsz, int xperm); + uint64 uvmdealloc(uint64 *pagetable, uint64 oldsz, uint64 newsz); + void uvmfree(uint64 *pagetable, uint64 sz); + int uvmcopy(uint64 *oldpt, uint64 *newpt, uint64 sz); + void uvmunmap(uint64 *pagetable, uint64 va, uint64 npages, int do_free); + + int copyout(uint64 *pagetable, uint64 dstva, char *src, uint64 len); + int copyin(uint64 *pagetable, char *dst, uint64 srcva, uint64 len); + int copyinstr(uint64 *pagetable, char *dst, uint64 srcva, uint64 max); + + int handle_cow_fault(uint64 *pagetable, uint64 va); + int mappages(uint64 *pagetable, uint64 va, uint64 size, uint64 pa, int perm); + + uint64 walkaddr(uint64 *pagetable, uint64 va); }
\ No newline at end of file diff --git a/include/kernel/pmm.h b/include/kernel/pmm.h index 3328450..d31473f 100644 --- a/include/kernel/pmm.h +++ b/include/kernel/pmm.h @@ -1,13 +1,50 @@ #pragma once #include "common/types.h" +#include "kernel/spinlock.h" -#define PHYSTOP (0x80000000 + 128 * 1024 * 1024) -#define PGSIZE 4096 +// Physical Memory Layout Definition +#define KERNBASE 0x80000000 +// Set the memory size to 128MB +#define PHYSIZE (128 * 1024 * 1024) +#define PHYSTOP (KERNBASE + PHYSIZE) -namespace PMM{ +// Buddy System Max Order +// Order 10 => 2^10 pages = 1024 * 4KB = 4MB contiguous block +// Order 11 => 2^11 pages = 8MB +#define MAX_ORDER 11 + +// Page flag +#define PG_used 0x01 // This page is occupied +#define PG_reserved 0x02 // This page is reserved (kernel code/data, DTB, MemMap) + +// Physical Page Metadata Structure +struct Page +{ + uint8 flags; // Page Status (PG_used, PG_reserved) + uint8 order; // If it is the first page of a free block, record the order of the current block + uint16 refcnt; // Reference Counting + struct Page *next; // Free list pointer (points to the next free block of the same size) + struct Page *prev; // Doubly linked list (easy to remove) +}; + +namespace PMM +{ + // Initialize physical memory management void init(); - void* alloc_page(); + // Core Allocation Interface (Buddy System) + // order: 0 ~ MAX_ORDER-1 + void *alloc_pages(int order); + void free_pages(void *pa, int order); + + // Compatible with old interface (allocate single page with order=0 and automatically reset to zero) + void *alloc_page(); + void free_page(void *pa); + + // Helper function + struct Page *pa_to_page(uint64 pa); + uint64 page_to_pa(struct Page *page); - void free_page(void* pa); + // Debug information + void print_free_lists(); }
\ No newline at end of file diff --git a/include/kernel/proc.h b/include/kernel/proc.h index b557267..cbf2902 100644 --- a/include/kernel/proc.h +++ b/include/kernel/proc.h @@ -1,56 +1,8 @@ #pragma once #include "common/types.h" -#include "kernel/trap.h" +#include "kernel/proc_def.h" #include "kernel/spinlock.h" -struct Context -{ - uint64 ra; - uint64 sp; - - uint64 s0; - uint64 s1; - uint64 s2; - uint64 s3; - uint64 s4; - uint64 s5; - uint64 s6; - uint64 s7; - uint64 s8; - uint64 s9; - uint64 s10; - uint64 s11; -}; - -enum ProcState{ - UNUSED, - USED, - SLEEPING, - RUNNABLE, - RUNNING, - ZOMBIE, -}; - -struct Proc -{ - struct Context context; - struct Trapframe *tf; - - uint64 *pagetable; - - enum ProcState state; - int pid; - - void* kstack; - char name[16]; - uint64 sz; - - struct Proc *parent; - void *chan; - int xstate; -}; - - namespace ProcManager { void init(); @@ -62,8 +14,11 @@ namespace ProcManager void user_init(); void exit(int status); int wait(uint64 addr); - void sleep(void *chan, struct spinlock *lk); + + void sleep(void *chan, Spinlock *lk); void wakeup(void *chan); + + int growproc(int n); } void forkret(); diff --git a/include/kernel/proc_def.h b/include/kernel/proc_def.h new file mode 100644 index 0000000..5ce4c03 --- /dev/null +++ b/include/kernel/proc_def.h @@ -0,0 +1,69 @@ +#pragma once +#include "common/types.h" +#include "common/fcntl.h" + +struct cpu; +struct Trapframe; +class Inode; +struct file; + +enum ProcState +{ + UNUSED, + USED, + SLEEPING, + RUNNABLE, + RUNNING, + ZOMBIE, +}; + +#define PRIO_MAX 0 +#define PRIO_MIN 3 +#define N_PRIO 4 + +// Context structure +struct Context +{ + uint64 ra; + uint64 sp; + uint64 s0; + uint64 s1; + uint64 s2; + uint64 s3; + uint64 s4; + uint64 s5; + uint64 s6; + uint64 s7; + uint64 s8; + uint64 s9; + uint64 s10; + uint64 s11; +}; + +struct Proc +{ + struct Context context; + struct Trapframe *tf; + uint64 *pagetable; + enum ProcState state; + int pid; + void *kstack; + char name[16]; + uint64 sz; + struct Proc *parent; + void *chan; + int xstate; + + Inode *cwd; + struct file *ofile[NOFILE]; + + struct Proc *next; + struct Proc *prev; + + struct Proc *all_next; + struct Proc *all_prev; + + int priority; + int ticks; + struct cpu *cpu; +};
\ No newline at end of file diff --git a/include/kernel/riscv.h b/include/kernel/riscv.h index 4b3d323..0c586a9 100644 --- a/include/kernel/riscv.h +++ b/include/kernel/riscv.h @@ -79,6 +79,7 @@ w_sip(uint64 x) // Supervisor Interrupt Enable #define SIE_SEIE (1L << 9) // external #define SIE_STIE (1L << 5) // timer +#define SIE_SSIE (1L << 1) // software static inline uint64 r_sie() { @@ -375,7 +376,8 @@ typedef uint64 *pagetable_t; // 512 PTEs #define PTE_X (1L << 3) #define PTE_U (1L << 4) // user can access - +// COW Flag (PTE bit 8) +#define PTE_COW (1L << 8) #if defined(LAB_MMAP) || defined(LAB_PGTBL) || defined(LAB_COW) #define PTE_LEAF(pte) (((pte) & PTE_R) | ((pte) & PTE_W) | ((pte) & PTE_X)) diff --git a/include/kernel/runqueue.h b/include/kernel/runqueue.h new file mode 100644 index 0000000..ea6e4cc --- /dev/null +++ b/include/kernel/runqueue.h @@ -0,0 +1,16 @@ +#pragma once +#include "kernel/spinlock.h" +#include "kernel/proc_def.h" + +struct RunQueue +{ + Spinlock lock; + struct Proc *heads[N_PRIO]; + struct Proc *tails[N_PRIO]; + uint64 count; + + void init(const char *name); + void enqueue(struct Proc *p); + struct Proc *dequeue(); + struct Proc *steal(); +};
\ No newline at end of file diff --git a/include/kernel/slab.h b/include/kernel/slab.h new file mode 100644 index 0000000..650f8a9 --- /dev/null +++ b/include/kernel/slab.h @@ -0,0 +1,10 @@ +#pragma once +#include "common/types.h" + +namespace Slab +{ + void init(); + + void *kmalloc(uint64 size); + void kfree(void *ptr); +}
\ No newline at end of file diff --git a/include/kernel/spinlock.h b/include/kernel/spinlock.h index aec4b6a..3102e68 100644 --- a/include/kernel/spinlock.h +++ b/include/kernel/spinlock.h @@ -1,20 +1,48 @@ #pragma once #include "common/types.h" -struct spinlock +struct cpu; + +// Spinlock +// Features: Non-interruptible, busy-wait loop, extremely short hold time. +// Scenario: Protecting core data such as scheduler, interrupt handling, PMM, etc. +class Spinlock { - uint locked; +public: + volatile uint32 locked; // Whether it is locked (0: unlocked, 1: locked) + const char *name; // Lock Name (for debugging) + struct cpu *cpu; // The CPU holding the lock + + // Constructor + Spinlock() : locked(0), name("uninit"), cpu(nullptr) {} + + // Initialize lock name + void init(const char *name); + + // Acquire lock + void acquire(); - char *name; - int cpu_id; + // Release lock + void release(); + + // Check if the current CPU holds the lock + bool holding(); }; -namespace Spinlock +// Mutex +// Features: Allows interruption, allows sleeping (yield CPU), can hold for a long time. +// Scenarios: File system IO, device driver waiting, long-term buffering operations. +class Mutex { - void init(struct spinlock *lk, char *name); - void acquire(struct spinlock *lk); - void release(struct spinlock *lk); - int holding(struct spinlock *lk); - void push_off(); - void pop_off(); -}
\ No newline at end of file +public: + uint32 locked; // Whether it is locked + const char *name; // Lock Name + int pid; // PID of the process holding the lock (for debugging) + + Spinlock lk; // Internal spin lock, protects the state of the Mutex itself + + void init(const char *name); + void acquire(); + void release(); + bool holding(); +};
\ No newline at end of file diff --git a/include/kernel/sync.h b/include/kernel/sync.h new file mode 100644 index 0000000..83d88c8 --- /dev/null +++ b/include/kernel/sync.h @@ -0,0 +1,38 @@ +#pragma once +#include "common/types.h" +#include "kernel/spinlock.h" +#include "kernel/proc_def.h" + + +struct WaitQueue +{ + Spinlock lock; + struct Proc *head; + struct Proc *tail; + + void init(const char *name); + void sleep(); + void wakeup(); + void wakeup_all(); +}; + +struct Semaphore +{ + int value; + WaitQueue wq; + Spinlock lock; + + void init(int val, const char *name); + void wait(); + void post(); +}; + +struct CondVar +{ + WaitQueue wq; + + void init(const char *name); + void wait(Spinlock *mutex_lock); + void signal(); + void broadcast(); +};
\ No newline at end of file diff --git a/include/kernel/syscall.h b/include/kernel/syscall.h index 222f2f4..c0c9082 100644 --- a/include/kernel/syscall.h +++ b/include/kernel/syscall.h @@ -22,6 +22,8 @@ #define SYS_link 20 #define SYS_mkdir 21 #define SYS_close 22 +#define SYS_disk_test 23 +#define SYS_lseek 24 #ifndef __ASSEMBLER__ void syscall(); diff --git a/include/kernel/trap.h b/include/kernel/trap.h index 79bb037..35b225c 100644 --- a/include/kernel/trap.h +++ b/include/kernel/trap.h @@ -45,7 +45,6 @@ struct Trapframe namespace Trap { void init(); - extern "C" void kernelvec(); - extern "C" void kerneltrap(struct Trapframe *tf); -} -extern "C" void usertrap();
\ No newline at end of file + void inithart(); + int devintr(); +}
\ No newline at end of file diff --git a/include/lib/string.h b/include/lib/string.h index aa87489..309bf02 100644 --- a/include/lib/string.h +++ b/include/lib/string.h @@ -10,4 +10,5 @@ void *memcpy(void *dst, const void *src, uint n); void strcpy(char *dst, const char *src); int strlen(const char *s); -int strcmp(const char *s1, const char *s2);
\ No newline at end of file +int strcmp(const char *s1, const char *s2); +int strcasecmp(const char *s1, const char *s2);
\ No newline at end of file diff --git a/include/ulib/assert.h b/include/ulib/assert.h new file mode 100644 index 0000000..a083aff --- /dev/null +++ b/include/ulib/assert.h @@ -0,0 +1,20 @@ +#ifndef _ASSERT_H +#define _ASSERT_H + +#ifdef NDEBUG +#define assert(ignore) ((void)0) +#else +#ifdef __cplusplus +extern "C" +{ +#endif + void __assert_fail(const char *expr, const char *file, int line, const char *func); +#ifdef __cplusplus +} +#endif + +#define assert(expr) \ + ((expr) ? (void)0 : __assert_fail(#expr, __FILE__, __LINE__, __func__)) +#endif + +#endif
\ No newline at end of file diff --git a/include/ulib/cstdlib b/include/ulib/cstdlib new file mode 100644 index 0000000..69b9bb6 --- /dev/null +++ b/include/ulib/cstdlib @@ -0,0 +1,52 @@ +#ifndef _CPP_CSTDLIB +#define _CPP_CSTDLIB + +#include "stdlib.h" + +namespace std +{ + using :: malloc; + using :: free; + using :: calloc; + using :: realloc; + + using :: exit; + using :: abort; + using :: atexit; + using :: getenv; + using :: system; + + using :: atoi; + using :: atol; + using :: atoll; + using :: atof; + + using :: strtol; + using :: strtoul; + using :: strtoll; + using :: strtoull; + + using :: strtod; + using :: strtof; + + using :: bsearch; + using :: qsort; + + using :: abs; + using :: labs; + using :: llabs; + using :: div; + using :: ldiv; + using :: lldiv; + + using :: rand; + using :: srand; + + using :: mblen; + using :: mbtowc; + using :: wctomb; + using :: mbstowcs; + using :: wcstombs; +} + +#endif
\ No newline at end of file diff --git a/include/ulib/cstring b/include/ulib/cstring new file mode 100644 index 0000000..bb8ab44 --- /dev/null +++ b/include/ulib/cstring @@ -0,0 +1,32 @@ +#ifndef _CPP_CSTRING +#define _CPP_CSTRING + +#include "string.h" + +namespace std +{ + using::memchr; + using::memcmp; + using::memcpy; + using::memmove; + using::memset; + using::strcat; + using::strchr; + using::strcmp; + using::strcpy; + using::strcspn; + using::strdup; + using::strerror; + using::strlcat; + using::strlcpy; + using::strlen; + using::strncat; + using::strncmp; + using::strpbrk; + using::strrchr; + using::strspn; + using::strstr; + using::strtok; +} // namespace std + +#endif
\ No newline at end of file diff --git a/include/ulib/ctypes.h b/include/ulib/ctypes.h new file mode 100644 index 0000000..3e0bc22 --- /dev/null +++ b/include/ulib/ctypes.h @@ -0,0 +1,26 @@ +#ifndef _CTYPE_H +#define _CTYPE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + static inline int isdigit(int c) { return c >= '0' && c <= '9'; } + static inline int islower(int c) { return c >= 'a' && c <= 'z'; } + static inline int isupper(int c) { return c >= 'A' && c <= 'Z'; } + static inline int isalpha(int c) { return islower(c) || isupper(c); } + static inline int isalnum(int c) { return isalpha(c) || isdigit(c); } + static inline int isspace(int c) { return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; } + static inline int isprint(int c) { return c >= 32 && c <= 126; } + static inline int iscntrl(int c) { return c < 32 || c == 127; } + static inline int isxdigit(int c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } + + static inline int tolower(int c) { return isupper(c) ? c + 32 : c; } + static inline int toupper(int c) { return islower(c) ? c - 32 : c; } + +#ifdef __cplusplus +} +#endif + +#endif
\ No newline at end of file diff --git a/include/ulib/cxx.cc b/include/ulib/cxx.cc new file mode 100644 index 0000000..d24de1e --- /dev/null +++ b/include/ulib/cxx.cc @@ -0,0 +1,32 @@ +#include "ulib/stdlib.h" + + +void *operator new(unsigned long n) +{ + return malloc(n); +} + +void *operator new[](unsigned long n) +{ + return malloc(n); +} + +void operator delete(void *p) +{ + free(p); +} + +void operator delete[](void *p) +{ + free(p); +} + +void operator delete(void *p, unsigned long) +{ + free(p); +} + +void operator delete[](void *p, unsigned long) +{ + free(p); +}
\ No newline at end of file diff --git a/include/ulib/errno.h b/include/ulib/errno.h new file mode 100644 index 0000000..03ff8fd --- /dev/null +++ b/include/ulib/errno.h @@ -0,0 +1,31 @@ +#ifndef _ERRNO_H +#define _ERRNO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern int errno; + +// Linux/ASM-Generic +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ + +#ifdef __cplusplus +} +#endif + +#endif
\ No newline at end of file diff --git a/include/ulib/iostream b/include/ulib/iostream new file mode 100644 index 0000000..83ed74c --- /dev/null +++ b/include/ulib/iostream @@ -0,0 +1,110 @@ +#ifndef _IOSTREAM +#define _IOSTREAM + +#include "stdio.h" + +namespace std +{ + class ostream + { + private: + FILE* _stream; + public: + ostream(FILE* f) : _stream(f) {} + + ostream& operator<<(const char* str) + { + fprintf(_stream, "%s", str); + return *this; + } + + ostream& operator<<(int n) + { + fprintf(_stream, "%d", n); + return *this; + } + + ostream &operator<<(long n) + { + fprintf(_stream, "%ld", n); + return *this; + } + + ostream &operator<<(long long n) + { + fprintf(_stream, "%lld", n); + return *this; + } + + ostream &operator<<(double f) + { + fprintf(_stream, "%f", f); + return *this; + } + + ostream &operator<<(char c) + { + fputc(c, _stream); + return *this; + } + + ostream &operator<<(void *p) + { + fprintf(_stream, "%p", p); + return *this; + } + + ostream &operator<<(ostream &(*func)(ostream &)) + { + return func(*this); + } + + ostream &flush() + { + fflush(_stream); + return *this; + } + }; + + class istream{ + private: + FILE* _stream; + public: + istream(FILE *f) : _stream(f) {} + + istream& operator>>(int n) + { + fscanf(_stream, "%d", n); + return *this; + } + + istream &operator>>(double &f) + { + fscanf(_stream, "%lf", &f); + return *this; + } + + istream &operator>>(char *str) + { + fscanf(_stream, "%s", str); + return *this; + } + + istream &operator>>(char &c) + { + char tmp[2]; + fscanf(_stream, " %c", tmp); + c = tmp[0]; + return *this; + } + }; + + inline ostream &endl(ostream &os) + { + os << '\n'; + os.flush(); + return os; + } +} + +#endif
\ No newline at end of file diff --git a/include/ulib/limits.h b/include/ulib/limits.h new file mode 100644 index 0000000..030ffbb --- /dev/null +++ b/include/ulib/limits.h @@ -0,0 +1,22 @@ +#pragma once + +#define CHAR_BIT 8 +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 +#define UCHAR_MAX 255 + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define USHRT_MAX 65535 + +#define INT_MIN (-2147483648) +#define INT_MAX 2147483647 +#define UINT_MAX 4294967295U + +#define LONG_MAX 9223372036854775807L +#define LONG_MIN (-LONG_MAX - 1L) +#define ULONG_MAX 18446744073709551615UL + +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LLONG_MAX - 1LL) +#define ULLONG_MAX 18446744073709551615ULL
\ No newline at end of file diff --git a/include/ulib/stb_sprintf.h b/include/ulib/stb_sprintf.h new file mode 100644 index 0000000..00dc123 --- /dev/null +++ b/include/ulib/stb_sprintf.h @@ -0,0 +1,1916 @@ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) + #if defined(__has_feature) && defined(__has_attribute) + #if __has_feature(address_sanitizer) + #if __has_attribute(__no_sanitize__) + #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) + #elif __has_attribute(__no_sanitize_address__) + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #elif __has_attribute(__no_address_safety_analysis__) + #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) + #endif + #endif + #endif +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) + #if __has_attribute(format) + #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) + #endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include <stdarg.h> // for va_arg(), va_list() +#include <stddef.h> // size_t, ptrdiff_t + +#ifndef stbsp__uintptr +#if defined(__riscv) && (__riscv_xlen == 64) +#define stbsp__uintptr unsigned long long +#elif defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) +#define stbsp__uintptr unsigned long long +#else +#define stbsp__uintptr unsigned int +#endif +#endif + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) +{ + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff + #define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } + #define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } + #define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer + #define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; + #ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else + #endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { + #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); +// add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } +endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + +done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) +{ + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) +{ + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U +}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + stbsp__int64 bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } + +#define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (stbsp__int64)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (stbsp__int64)(ahi + alo + xl); \ + } + +#define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) +{ + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/include/ulib/stdio.h b/include/ulib/stdio.h new file mode 100644 index 0000000..2e19b87 --- /dev/null +++ b/include/ulib/stdio.h @@ -0,0 +1,97 @@ +/* Standard I/O Library for Lume OS */ + +#ifndef _STDIO_H +#define _STDIO_H + +#include <stdarg.h> +#include "ulib/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*Type Definition (Adjusted According to Architecture)*/ +#ifndef NULL +#define NULL ((void *)0) +#endif + +/*Macro definition*/ +#define EOF (-1) + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define BUFSIZ 1024 + + +/* FILE structure */ +typedef struct +{ + int fd; + int flags; + int error; + int eof; + + char buffer[BUFSIZ]; + + int buf_write_pos; + + int buf_read_pos; + int buf_read_cnt; + + int buf_mode; +} FILE; + +/* standard stream object */ +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; + +/* Function Declaration */ + +/* File Operations */ +FILE *fopen(const char *filename, const char *mode); +int fclose(FILE *stream); +int fflush(FILE *stream); // Not implement + +/* Formatted input and output */ +int printf(const char *format, ...); +int scanf(const char *format, ...); +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, uint64 size, const char *format, ...); +int vsnprintf(char *str, uint64 size, const char *format, va_list ap); +int sscanf(const char *str, const char *format, ...); + +int fprintf(FILE *stream, const char *format, ...); +int fscanf(FILE *stream, const char *format, ...); + +/* Character/String Read and Write */ +int fputc(int c, FILE *stream); +int fputs(const char *s, FILE *stream); +int fgetc(FILE *stream); +char *fgets(char *str, int n, FILE *stream); +int ungetc(int c, FILE *stream); + +/* Common Macros/Wrapper Functions */ +#define getchar() fgetc(stdin) +#define putchar(c) fputc((c), stdout) + +/* Binary read and write */ +uint64 fread(void *ptr, uint64 size, uint64 nmemb, FILE *stream); +uint64 fwrite(const void *ptr, uint64 size, uint64 nmemb, FILE *stream); + +/* File location */ +int fseek(FILE *stream, long offset, int whence); +long ftell(FILE *stream); +void rewind(FILE *stream); + +/* Status Inquiry */ +int feof(FILE *stream); +int ferror(FILE *stream); +void clearerr(FILE *stream); + +#ifdef __cplusplus +} +#endif +#endif /* _STDIO_H */
\ No newline at end of file diff --git a/include/ulib/stdlib.h b/include/ulib/stdlib.h new file mode 100644 index 0000000..7dc325e --- /dev/null +++ b/include/ulib/stdlib.h @@ -0,0 +1,95 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include "ulib/types.h" +#include "ulib/limits.h" +#include "ulib/errno.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define ERANGE 34 /* Math result not representable */ +#define ENOMEM 12 /* Out of memory */ + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 +#define RAND_MAX 32767 +#define MB_CUR_MAX 1 + +typedef struct +{ + int quot; + int rem; +} div_t; +typedef struct +{ + long quot; + long rem; +} ldiv_t; +typedef struct +{ + long long quot; + long long rem; +} lldiv_t; + +typedef int (*__compar_fn_t)(const void *, const void *); + +/* Memory Management */ +void *malloc(size_t size); +void free(void *ptr); +void *calloc(size_t nmemb, size_t size); +void *realloc(void *ptr, size_t size); + +/* Process Control */ +int exit(int status); +void abort(void); +int atexit(void (*function)(void)); +char *getenv(const char *name); +int system(const char *command); + +/* String Conversion */ +int atoi(const char *nptr); +long atol(const char *nptr); +long long atoll(const char *nptr); +double atof(const char *nptr); + +long strtol(const char *nptr, char **endptr, int base); +unsigned long strtoul(const char *nptr, char **endptr, int base); +long long strtoll(const char *nptr, char **endptr, int base); +unsigned long long strtoull(const char *nptr, char **endptr, int base); + +double strtod(const char *nptr, char **endptr); +float strtof(const char *nptr, char **endptr); + +/* Search & Sort*/ +void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); +void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); + +/* Math & Mics */ +int abs(int j); +long labs(long j); +long long llabs(long long j); +div_t div(int numer, int denom); +ldiv_t ldiv(long numer, long denom); +lldiv_t lldiv(long long numer, long long denom); + +int rand(void); +void srand(unsigned int seed); + +int mblen(const char *s, size_t n); +int mbtowc(wchar_t *pwc, const char *s, size_t n); +int wctomb(char *s, wchar_t wchar); +size_t mbstowcs(wchar_t *dest, const char *src, size_t n); +size_t wcstombs(char *dest, const wchar_t *src, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif /* _STDLIB_H */
\ No newline at end of file diff --git a/include/ulib/string.h b/include/ulib/string.h new file mode 100644 index 0000000..6e87157 --- /dev/null +++ b/include/ulib/string.h @@ -0,0 +1,70 @@ +#ifndef _STRING_H +#define _STRING_H + +#include "ulib/types.h" +#include "ulib/errno.h" + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned long long size_t; +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Memory Manipulation*/ + void *memcpy(void *dest, const void *src, size_t n); + + void *memmove(void *dest, const void *src, size_t n); + + void *memset(void *dest, int c, size_t n); + + int memcmp(const void *s1, const void *s2, size_t n); + + void *memchr(const void *s, int c, size_t n); + + /* String Manipulation */ + + size_t strlen(const char *s); + + char *strcpy(char *dest, const char *src); + + char *strncpy(char *dest, const char *src, size_t n); + + char *strcat(char *dest, const char *src); + + char *strncat(char *dest, const char *src, size_t n); + + int strcmp(const char *s1, const char *s2); + + int strncmp(const char *s1, const char *s2, size_t n); + + char *strchr(const char *s, int c); + + char *strrchr(const char *s, int c); + + char *strstr(const char *haystack, const char *needle); + + char *strdup(const char *s); + + size_t strlcpy(char *dest, const char *src, size_t size); + + size_t strlcat(char *dest, const char *src, size_t size); + + size_t strspn(const char *s, const char *accept); + + size_t strcspn(const char *s, const char *reject); + + char *strpbrk(const char *s, const char *accept); + + char *strtok(char *str, const char *delim); + + char *strerror(int errnum); + +#ifdef __cplusplus +} +#endif + +#endif /* _STRING_H */
\ No newline at end of file diff --git a/include/ulib/types.h b/include/ulib/types.h new file mode 100644 index 0000000..2f15bf1 --- /dev/null +++ b/include/ulib/types.h @@ -0,0 +1,21 @@ +#ifndef _ULIB_TYPES_H +#define _ULIB_TYPES_H + +#include "common/types.h" + +#ifndef _ULIB_ALIASES +#define _ULIB_ALIASES +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef __cplusplus +typedef int wchar_t; +#endif + +#endif
\ No newline at end of file diff --git a/include/user/user.h b/include/user/user.h index 7ab4923..7624a21 100644 --- a/include/user/user.h +++ b/include/user/user.h @@ -24,6 +24,7 @@ extern "C" { int sleep(int ticks); int uptime(); - + // Debug void putc(char c); + int disk_test(); }
\ No newline at end of file |
