
header "sys" as sys

+ fn alloc(size: uint) ptr {
    return sys:malloc(size)
}

+ fn free(adr: ptr) {
    sys:free(adr)
}

+ fn calloc(size: uint) ptr {
    let adr = alloc(size)
    clear(adr, size)
    return adr
}

+ fn copy(from: ptr, to: ptr, length: uint) {
    if length == 0 : return
    @memcpy(from, to, length)
}

+ fn resize(adr: ptr, size: uint, new_size: uint) ptr {
    if new_size == size : return adr
    if new_size < size : size = new_size
    let new = alloc(new_size)
    @memcpy(adr, new, size)
    free(adr)
    return new
}

+ fn clear(adr: ptr, length: uint) {
    let i : uint = 0
    let chunks = length / size_of(uint)
    while i < chunks {
        @ptrv(adr, uint, i) = 0
        i++
    }
    i = i * size_of(uint)
    while i < length {
        @ptrv(adr, u8, i) = 0
        i++
    }
}

+ fn equal(a: ptr, b: ptr, length: uint) bool {
    let chunks = length / size_of(ptr)
    let i : uint = chunks
    while i > 0 {
        i--
        let av = @ptrv(a, uint, i)
        let bv = @ptrv(b, uint, i)
        if av != bv : return false
    }
    i = length % size_of(ptr)
    let offset = chunks * size_of(ptr)
    let ao = a + offset
    let bo = b + offset
    while i > 0 {
        i--
        let av = @ptrv(ao, u8, i)
        let bv = @ptrv(bo, u8, i)
        if av != bv : return false
    }
    return true
}

+ fn ascii_bytes_to_lower(adr: ptr, len: uint) {
    while len-- > 0 {
        let ch = @ptrv(adr, u8, len)
        if ch > 64 && ch < 91 : @ptrv(adr, u8, len) = ch + 32
    }
}

+ fn bytes_to_uint(adr: ptr, len: uint) uint !not_a_number {
    let result : uint = 0
    let mult : uint = 1
    while len-- > 0 {
        let ch = @ptrv(adr, u8, len)
        if ch < 48 || ch > 57 : throw not_a_number
        result += ((ch.@cast(uint)) - 48) * mult
        mult *= 10
    }
    return result
}

+ fn find_char(adr: ptr, ch: u8, length: uint) uint !not_found {
    let i : uint = 0
    while i < length {
        let ii = i
        if @ptrv(adr, u8, ii) == ch : return ii
        i = ii + 1
    }
    throw not_found
}

fn find_char_(adr: ptr, ch: u8, length: uint) uint !not_found {
    let offset : uint = 0
    let end1 : uint = 8 - (adr % 8)
    //
    if length < end1 {
        end1 = length
    }
    while offset < end1 {
        if @ptrv(adr, u8, offset) == ch : return offset
        offset++
    }

    let end2 : uint = length - (length % 8)

    let v1 : u64 = ch << 56
    let v2 : u64 = v1 >> 8
    let v3 : u64 = v2 >> 8
    let v4 : u64 = v3 >> 8
    let v5 : u64 = v4 >> 8
    let v6 : u64 = v5 >> 8
    let v7 : u64 = v6 >> 8
    let v8 : u64 = ch
    while offset < end2 {
        let p = @ptrv(adr, u64, offset)
        let c1 = (p & v1) == v1
        let c2 = (p & v2) == v2
        let c3 = (p & v3) == v3
        let c4 = (p & v4) == v4
        let c5 = (p & v5) == v5
        let c6 = (p & v6) == v6
        let c7 = (p & v7) == v7
        let c8 = (p & v8) == v8
        let x = c1 || c2 || c3 || c4 || c5 || c6 || c7 || c8
        
        if x {
            if c1 : return offset
            if c2 : return offset + 1
            if c3 : return offset + 2
            if c4 : return offset + 3
            if c5 : return offset + 4
            if c6 : return offset + 5
            if c7 : return offset + 6
            if c8 : return offset + 7
        }

        offset += 8
    }
    while offset < length {
        if @ptrv(adr, u8, offset) == ch : return offset
        offset++
    }
    throw not_found
}

// GNU libc version for find_char aka. memchar (same performance, just here in case we need it)
fn gnu_libc_memchar(adr: ptr, ch: u8, length: uint) ptr !not_found {

    while (adr % size_of(ptr) != 0) {
      if @ptrv(adr, u8) == ch : return adr
      adr += 1
    }

    let rep_one : u64 = 0x0101010101010101
    let rep_ch1 : u64 = ch | (ch << 8) | (ch << 16) | (ch << 24)
    let rep_ch = rep_ch1 | rep_ch1 << 32

    while length >= size_of(u64) {
        let cmp = @ptrv(adr, u64) ^ rep_ch
        let inv : u64 = 0xffffffff - cmp

        if (((cmp - rep_one) & inv) & (rep_one << 7)) != 0 {
            break
        }
        adr += size_of(u64)
        length -= size_of(u64)
    }

    while length-- > 0 {
        if @ptrv(adr, u8) == ch : return adr
        adr += 1
    }

    throw not_found
}
