Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some bytes primitives (split and cleaned up from libuv) #652

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion kklib/include/kklib/bytes.h
Original file line number Diff line number Diff line change
@@ -157,7 +157,10 @@ 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];
}

/*--------------------------------------------------------------------------------------------------
Length, compare
@@ -192,6 +195,19 @@ static inline kk_bytes_t kk_bytes_copy(kk_bytes_t b, kk_context_t* ctx) {
}
}

static inline kk_bytes_t kk_bytes_set(kk_bytes_t bytes, uint64_t i, int8_t b, kk_context_t* ctx){
if (kk_datatype_ptr_is_unique(bytes, ctx)) {
uint8_t* buf = (uint8_t*)kk_bytes_buf_borrow(bytes, NULL, ctx);
buf[i] = (uint8_t)b;
return bytes;
} else {
kk_bytes_t bytes_new = kk_bytes_copy(bytes, ctx);
uint8_t* buf = (uint8_t*)kk_bytes_buf_borrow(bytes_new, NULL, ctx);
buf[i] = (uint8_t)b;
return bytes_new;
}
}

static inline bool kk_bytes_ptr_eq_borrow(kk_bytes_t b1, kk_bytes_t b2) {
return (kk_datatype_eq(b1, b2));
}
2 changes: 2 additions & 0 deletions lib/std/core.kk
Original file line number Diff line number Diff line change
@@ -25,6 +25,8 @@ pub import std/core/order
pub import std/core/char
pub import std/core/int
pub import std/core/vector
pub import std/core/bytes
pub import std/core/bslice
pub import std/core/string
pub import std/core/sslice
pub import std/core/list
102 changes: 102 additions & 0 deletions lib/std/core/bslice.kk
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------
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/core/bslice
import std/core/bytes
import std/core/types
import std/core/int
import std/core/exn

extern import
c file "inline/bslice"

// A byte slice
pub value 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)

// Assigns the bytes starting at offset `i` to `new-value`, throws if there is not enough space or the index is invalid
pub fun assign( b : bytes, i : int, new-value : bslice ) : exn bytes
val l = b.length.int
val size = new-value.len
if i < 0 || i > l then
throw("index out of bounds", ExnRange)
elif size + i > l then
throw("size overflow", ExnRange)
else
assign(b, i.ssize_t, new-value)

extern slice/assign(b : bytes, i : ssize_t, new-value : bslice ): bytes
c "kk_bslice_assign"
60 changes: 60 additions & 0 deletions lib/std/core/bytes.kk
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------
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/core/bytes
import std/core/types
import std/core/exn
import std/core/bool
import std/core/int

// 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 ) : bytes
c "kk_bytes_set"
js inline "#1[#2] = #3"
24 changes: 24 additions & 0 deletions lib/std/core/inline/bslice.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
kk_bytes_t kk_bslice_assign( kk_bytes_t bytes, kk_ssize_t i, struct kk_std_core_bslice_Bslice bslice, kk_context_t* ctx ){
kk_ssize_t len;
const uint8_t* bslice_buf = kk_bytes_buf_borrow(bslice.bytes, &len, ctx);
kk_ssize_t slice_len = kk_integer_clamp_ssize_t_borrow(bslice.len, ctx);
kk_ssize_t slice_start = kk_integer_clamp_ssize_t_borrow(bslice.start, ctx);
if (kk_datatype_ptr_is_unique(bytes, ctx)) {
kk_info_message("kk_bslice_assign: unique\n");
uint8_t* buf = (uint8_t*)kk_bytes_buf_borrow(bytes, &len, ctx);
for (kk_ssize_t j = 0; j < slice_len; j++) {
buf[i + j] = bslice_buf[slice_start + j];
}
kk_std_core_bslice__bslice_drop(bslice,ctx);
return bytes;
} else {
kk_info_message("kk_bslice_assign: not unique\n");
kk_bytes_t bytes_new = kk_bytes_copy(bytes, ctx);
uint8_t* buf_new = (uint8_t*)kk_bytes_buf_borrow(bytes_new, NULL, ctx);
for (kk_ssize_t j = 0; j < slice_len; j++) {
buf_new[i + j] = bslice_buf[slice_start + j];
}
kk_std_core_bslice__bslice_drop(bslice,ctx);
return bytes_new;
}
}
11 changes: 11 additions & 0 deletions lib/std/core/inline/bslice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

/*---------------------------------------------------------------------------
Copyright 2020-2024, Microsoft Research, Daan Leijen. 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.
---------------------------------------------------------------------------*/

struct kk_std_core_bslice_Bslice;
kk_bytes_t kk_bslice_assign( kk_bytes_t bytes, kk_ssize_t i, struct kk_std_core_bslice_Bslice bslice, kk_context_t* ctx );
13 changes: 7 additions & 6 deletions lib/std/core/sslice.kk
Original file line number Diff line number Diff line change
@@ -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"`,
@@ -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.
@@ -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)

3 changes: 3 additions & 0 deletions lib/std/core/types.kk
Original file line number Diff line number Diff line change
@@ -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

24 changes: 14 additions & 10 deletions src/Backend/C/FromCore.hs
Original file line number Diff line number Diff line change
@@ -797,7 +797,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
@@ -814,7 +814,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])
@@ -1088,7 +1088,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) $
@@ -1139,6 +1139,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()"

@@ -1326,6 +1327,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)
@@ -2225,12 +2228,13 @@ 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
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
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
@@ -2253,7 +2257,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

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

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

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

7 changes: 7 additions & 0 deletions test/lib/bytes.kk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


fun main()
val b1 = "Hello There".bytes
val new = "World".bytes.slice
val b2 = b1.assign(6, new)
println(b2.string)
1 change: 1 addition & 0 deletions test/lib/bytes.kk.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello World