Skip to content

Commit

Permalink
Add some bytes primitives
Browse files Browse the repository at this point in the history
  • Loading branch information
TimWhiting committed Dec 31, 2024
1 parent 7c4db5a commit 60d5771
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 17 deletions.
8 changes: 8 additions & 0 deletions kklib/include/kklib/bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,15 @@ static inline const char* kk_bytes_cbuf_borrow(const kk_bytes_t b, kk_ssize_t* l
return (const char*)kk_bytes_buf_borrow(b, len, ctx);
}

static inline int8_t kk_bytes_at(kk_bytes_t p, uint64_t i, kk_context_t* ctx){
const uint8_t* buf = kk_bytes_buf_borrow(p, NULL, ctx);
return (int8_t)buf[i];
}

static inline void kk_bytes_set(kk_bytes_t p, uint64_t i, int8_t b, kk_context_t* ctx){
uint8_t* buf = (uint8_t*)kk_bytes_buf_borrow(p, NULL, ctx);
buf[i] = (uint8_t)b;
}

/*--------------------------------------------------------------------------------------------------
Length, compare
Expand Down
13 changes: 7 additions & 6 deletions lib/std/core/sslice.kk
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ pub extern common-prefix(s : string, t : string, ^upto : int = -1 ) : sslice

// O(`count`). Advance the start position of a string slice by `count` characters
// up to the end of the string.
// A negative `count` advances the start position backwards upto the first position
// A negative `count` advances the start position backwards up to the first position
// in a string.
// Maintains the character count of the original slice upto the end of the string.
// Maintains the character count of the original slice up to the end of the string.
// For example:
//
// * `"abc".first.advance(1).string == "b"`,
Expand All @@ -166,16 +166,17 @@ pub extern extend( slice : sslice, ^count : int ) : sslice
js "_sslice_extend"

// O(1). Return the string slice from the start of a string up to the
// start of `slice` argument.
// start of the `slice` argument.
pub fun before(slice : sslice) : sslice
val Sslice(s,start,_len) = slice
Sslice(s,0,start)

// O(1). Return the string slice from the end of `slice` argument
// O(1). Return the string slice from the end of the `slice` argument
// to the end of the string.
pub fun after(slice : sslice) : sslice
val Sslice(s,start,len) = slice
Sslice(s,start+len,s.length - (start+len))
val new-start = start+len
Sslice(s,new-start,s.length - new-start)

// O(n). Copy the `slice` argument into a fresh string.
// Takes O(1) time if the slice covers the entire string.
Expand All @@ -191,7 +192,7 @@ pub extern next( slice : sslice ) : maybe<(char,sslice)>
cs "Primitive.SliceNext"
js "_sslice_next"

// Truncates a slice to length 0
// Truncates the slice to length 0
pub fun truncate( slice: sslice ): sslice
Sslice(slice.str, slice.start, 0)

Expand Down
3 changes: 3 additions & 0 deletions lib/std/core/types.kk
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ pub value type float32
// See `module std/core/vector` for vector operations.
pub type vector<a>

// A raw wrapper around a uint8 character array.
pub type bytes

// An any type. Used for external calls.
pub type any

Expand Down
82 changes: 82 additions & 0 deletions lib/std/data/bslice.kk
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*---------------------------------------------------------------------------
Copyright 2024, Tim Whiting.

This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the LICENSE file at the root of this distribution.
---------------------------------------------------------------------------*/

// Byte slices
module std/data/bslice
import std/data/bytes

// A byte slice
pub struct bslice
bytes: bytes
start: int
len: int // The length of the slice
total-len: int // Cached length of the backing byte array

// Create a byte slice from bytes
pub fun slice(b: bytes): bslice
val len = b.length.int
Bslice(b, 0, len, len)

// O(1). Advance the start position of a byte slice by `count` bytes
// up to the end of the byte array.
// A negative `count` advances the start position backwards up to index 0.
pub fun advance(b: bslice, n: int): bslice
val Bslice(bts, start, len, tl) = b
if n > 0 then
if n > len then // If advancing past the current length, advance to the end
Bslice(bts, start + len, 0, tl)
else
Bslice(bts, start + n, len - n, tl)
elif n < 0 then
if start + n < 0 then // If advancing past the beginning, advance to the beginning
Bslice(bts, 0, len + start, tl)
else
Bslice(bts, start + n, len - n, tl)
else
Bslice(bts, start, len, tl)

// O(1). Extend a byte slice by `count` bytes up to the end of the byte slice.
// A negative `count` shrinks the slice up to the empty slice.
pub fun extend(b: bslice, n: int): bslice
val Bslice(bts, start, len, tl) = b
if n > 0 then
if n > (tl - len) then // If extending past total end of bytes, extend to end of bytes
Bslice(bts, start, tl - start, tl)
else
Bslice(bts, start, len + n, tl)
elif n < 0 then
if len + n < 0 then // If extending past start of slice, extend to start point
Bslice(bts, start, 0, tl)
else
Bslice(bts, start, len + n, tl)
else
Bslice(bts, start, len, tl)

// Truncates the slice to length 0
pub fun truncate(b: bslice): bslice
val Bslice(bts, start, _, tl) = b
Bslice(bts, start, 0, tl)

// O(1). Return the byte slice from the start of the byte array up to the
// start of the `slice` argument.
pub fun before(slice: bslice): bslice
val Bslice(bts, start, _, tl) = slice
Bslice(bts, 0, start, tl)

// O(1). Return the byte slice from the end of the `slice` argument
// to the end of the byte array.
pub fun after(slice: bslice): bslice
val Bslice(bts, start, len, tl) = slice
val new-start = start + len
Bslice(bts, new-start, tl - new-start, tl)

// Get's the byte at the offset `i`
pub fun @index( ^b : bslice, i : int ) : exn int8
val Bslice(bts, start, len, _) = b
if i < 0 || i >= len then throw("index out of bounds", ExnRange)
bts.unsafe-index((start + i).ssize_t)
56 changes: 56 additions & 0 deletions lib/std/data/bytes.kk
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*---------------------------------------------------------------------------
Copyright 2024, Tim Whiting.

This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the LICENSE file at the root of this distribution.
---------------------------------------------------------------------------*/

// Byte arrays
module std/data/bytes

// Create a new empty array of bytes
pub extern empty(): bytes
c inline "kk_bytes_empty()"
js inline "new Uint8Array(0)"

// Allocate a new zero initialized array of `n` bytes.
pub extern alloc( n : ssize_t ) : bytes
c inline "kk_bytes_alloc_buf(#1, NULL, kk_context())"
js inline "new Uint8Array(#1).fill(#2)"

// Converts an array of bytes to a string. (Unsafe, ensure that the bytes have a zero terminated string)
pub extern string( bytes : bytes ) : string
c "kk_string_convert_from_qutf8"
js inline "String.fromCharCode.apply(null, #1)"

// Gets a view of the string as raw bytes
pub extern string/bytes( s : string ) : bytes
c inline "#1.bytes"
js inline "new TextEncoder().encode(#1)"

// Gets the length of the byte array
pub extern length( ^b : bytes ) : ssize_t
c "kk_bytes_len_borrow"
js inline "#1.length"

// Adjusts the byte array to be the size of the `len` argument
pub extern adjust-length(b: bytes, len: ssize_t): bytes
c "kk_bytes_adjust_length"
js "#1"

// Get's the byte at the offset `i`
pub fun @index(^b: bytes, i: int): exn int8
if i < 0 || i >= b.length.int then throw("index out of bounds", ExnRange)
b.unsafe-index(i.ssize_t)

// Get's the byte at the offset `i`, without checking the offset is valid
// !! Attention !! Unsafe API
pub extern unsafe-index( ^b : bytes, i : ssize_t ) : int8
c "kk_bytes_at"
js inline "#1[#2]"

// Assigns the byte at offset `i` to `new-value`
pub extern byte/assign( ^b : bytes, i : ssize_t, new-value : int8 ) : ()
c "kk_bytes_set"
js inline "#1[#2] = #3"
25 changes: 15 additions & 10 deletions src/Backend/C/FromCore.hs
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ genBoxCall tp arg
ctx = contextDoc
in case cType tp of
CFun _ _ -> primName_t prim "function_t" <.> tupled ([arg,ctx])
CPrim val | val == "kk_unit_t" || val == "bool" || val == "kk_string_t" -- || val == "kk_integer_t"
CPrim val | val == "kk_unit_t" || val == "bool" || val == "kk_string_t" || val == "kk_bytes_t" -- || val == "kk_integer_t"
-> primName_t prim val <.> parens arg -- no context
CData name -> primName prim (ppName name) <.> tupled [arg,ctx]
_ -> primName_t prim (show (ppType tp)) <.> tupled [arg,ctx] -- kk_box_t, int32_t
Expand All @@ -810,7 +810,7 @@ genUnboxCall tp arg argBorrow
ctx = contextDoc
in case cType tp of
CFun _ _ -> primName_t prim "function_t" <.> tupled [arg,ctx] -- no borrow
CPrim val | val == "kk_unit_t" || val == "bool" || val == "kk_string_t"
CPrim val | val == "kk_unit_t" || val == "bool" || val == "kk_string_t" || val == "kk_bytes_t"
-> primName_t prim val <.> parens arg -- no borrow, no context
| otherwise
-> primName_t prim val <.> tupled ([arg] ++ (if (cPrimCanBeBoxed val) then [argBorrow] else []) ++ [ctx])
Expand Down Expand Up @@ -1084,7 +1084,7 @@ genDupDropCallX prim tp args
= case cType tp of
CFun _ _ -> [(primName_t prim "function_t") <.> args]
CBox -> [(primName_t prim "box_t") <.> args]
CPrim val | val == "kk_integer_t" || val == "kk_string_t" || val == "kk_vector_t" || val == "kk_evv_t" || val == "kk_ref_t" || val == "kk_reuse_t" || val == "kk_box_t"
CPrim val | val == "kk_integer_t" || val == "kk_bytes_t" || val == "kk_string_t" || val == "kk_vector_t" || val == "kk_evv_t" || val == "kk_ref_t" || val == "kk_reuse_t" || val == "kk_box_t"
-> [(primName_t prim val) <.> args]
| otherwise
-> -- trace ("** skip dup/drop call: " ++ prim ++ ": " ++ show args) $
Expand Down Expand Up @@ -1135,6 +1135,7 @@ genHoleCall tp = -- ppType tp <.> text "_hole()")
case cType tp of
CPrim "kk_integer_t" -> text "kk_integer_zero"
CPrim "kk_string_t" -> text "kk_string_empty()"
CPrim "kk_bytes_t" -> text "kk_bytes_empty()"
CPrim "kk_vector_t" -> text "kk_vector_empty()"
_ -> text "kk_datatype_null()"

Expand Down Expand Up @@ -1322,6 +1323,8 @@ cTypeCon c
then CPrim "kk_integer_t"
else if (name == nameTpString)
then CPrim "kk_string_t"
else if (name == nameTpBytes)
then CPrim "kk_bytes_t"
else if (name == nameTpVector)
then CPrim "kk_vector_t"
else if (name == nameTpEvv)
Expand Down Expand Up @@ -2221,12 +2224,14 @@ genExprExternal tname formats [fieldDoc,argDoc] | getName tname == nameCFieldSet

-- normal external
genExprExternal tname formats argDocs0
= let name = getName tname
format = getFormat tname formats
argDocs = map (\argDoc -> if (all (\c -> isAlphaNum c || c == '_') (asString argDoc)) then argDoc else parens argDoc) argDocs0
in return $ case map (\fmt -> ppExternalF name fmt argDocs) $ lines format of
[] -> ([],empty)
ds -> (init ds, last ds)
= do
ctarget <- getCTarget
let name = getName tname
format = getFormat ctarget tname formats
argDocs = map (\argDoc -> if (all (\c -> isAlphaNum c || c == '_') (asString argDoc)) then argDoc else parens argDoc) argDocs0
return $ case map (\fmt -> ppExternalF name fmt argDocs) $ lines format of
[] -> ([],empty)
ds -> (init ds, last ds)
where
ppExternalF :: Name -> String -> [Doc] -> Doc
ppExternalF name [] args
Expand All @@ -2249,7 +2254,7 @@ getFormat :: TName -> [(Target,String)] -> String
getFormat tname formats
= case lookupTarget (C CDefault) formats of -- TODO: pass real ctarget from flags
Nothing -> -- failure ("backend does not support external in " ++ show tname ++ ": " ++ show formats)
trace( "warning: C backend does not support external in " ++ show tname ) $
trace( "warning: C backend does not support external in " ++ show tname ++ " looking in " ++ show formats ) $
("kk_unsupported_external(\"" ++ (show tname) ++ "\")")
Just s -> s

Expand Down
3 changes: 2 additions & 1 deletion src/Common/NamePrim.hs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ module Common.NamePrim

, nameTpBool, nameTpInt, nameTpChar
, nameTpFloat, nameTpFloat32, nameTpFloat16
, nameTpString
, nameTpString, nameTpBytes
-- , nameTpByte
, nameTpInt8, nameTpInt16, nameTpInt32, nameTpInt64
, nameTpSSizeT,nameTpIntPtrT
Expand Down Expand Up @@ -467,6 +467,7 @@ nameTpFloat16 = coreTypesName "float16"

nameTpChar = coreTypesName "char"
nameTpString = coreTypesName "string"
nameTpBytes = coreTypesName "bytes"
nameTpAny = coreTypesName "any"
nameTpVector = coreTypesName "vector"

Expand Down

0 comments on commit 60d5771

Please sign in to comment.