diff --git a/clang/docs/BSC/BiShengCLanguageUserManual.md b/clang/docs/BSC/BiShengCLanguageUserManual.md index 8429794bf00cef65b895b1f3741f90c0c15b7228..74403391778eef9e6573b4576ce4bb59e2c6f937 100644 --- a/clang/docs/BSC/BiShengCLanguageUserManual.md +++ b/clang/docs/BSC/BiShengCLanguageUserManual.md @@ -5291,6 +5291,75 @@ String new_s = world.slice(1, 4); 注:`bsc_string_no_pos`实际上是一个很大的 size_t 类型的值,即 SIZE_MAX。 +#### `Slice` + +`Slice`是 BiShengC 语言提供的不可变切片类型,用于对一段连续的且类型相同的内存进行读操作。其使用示例如下: + +```c +#include "slice.hbs" + +int main() { + int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Slice slice = Slice::new(&const *arr, 5); + const int *borrow _Nonnull first = slice.get(2); + if (*first == 3) { + printf("The third element of the slice is 3\n"); + } + return 0; +} +``` + +索引: +`Slice`目前不支持直接使用索引进行访问,但其提供了`get`等方法,可以传入想要访问的元素的索引,通过函数调用的方式进行访问。在访问`Slice`的元素时,会做**边界检查**。 + +`Slice`提供的对外接口及相应的使用用例如下: + +|对外接口|接口功能|代码示例| +|---|---|---| +|`unsafe Slice Slice::new(const T *borrow _Nonnull ptr, size_t len)`|根据指针和长度创建切片|Slice slice = Slice::new(&const *arr, 5);| +|`safe Slice Slice::sub(This this, size_t start, size_t end)`|从现有的切片中创建子切片|Slice sub = slice.sub(0, 5);| +|`safe const T *borrow _Nonnull Slice::get(This this, size_t index)`|返回切片中下标为index的元素的不可变借用|const int *borrow _Nonnull first = slice.get(0);| +|`safe size_t Slice::length(This this)`|返回切片长度|size_t len = slice.length();| +|`safe _Bool Slice::is_empty(This this)`|判断切片是否为空|_Bool is_empty = slice.is_empty();| +|`unsafe const T *_Nonnull Slice::as_ptr(This this)`|返回切片中保存的裸指针|const T *_Nonnull ptr = slice.as_ptr();| + + +#### `SliceMut` + +`SliceMut`是 BiShengC 语言提供的可变切片类型,用于对一段连续的且类型相同的内存进行读写操作。其使用示例如下: + +```c +#include "slice.hbs" + +int main() { + int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + SliceMut slice = SliceMut::new(&const *arr, 5); + const int *borrow _Nonnull first = slice.get(2); + *first = 5; + if (*first == 5) { + printf("The third element of the slice is 5\n"); + } + return 0; +} +``` + +索引: +`SliceMut`目前不支持直接使用索引进行访问,但其提供了`set`和`get`等方法,可以传入想要访问的元素的索引,通过函数调用的方式进行访问。在访问`SliceMut`的元素时,会做**边界检查**。 + +`SliceMut`提供的对外接口及相应的使用用例如下: + +|对外接口|接口功能|代码示例| +|---|---|---| +|`unsafe SliceMut SliceMut::new(T *borrow _Nonnull ptr, size_t len)`|根据指针和长度创建可变切片|SliceMut slice = SliceMut::new(&mut *arr, 5);| +|`safe Slice SliceMut::sub(This this, size_t start, size_t end)`|从现有的切片中创建不可变子切片|Slice sub = slice.sub(0, 4);| +|`safe SliceMut SliceMut::sub_mut(This this, size_t start, size_t end)`|从现有的切片中创建可变子切片|SliceMut sub_mut = slice.sub_mut(0, 4);| +|`safe const T *borrow _Nonnull SliceMut::get(This this, size_t index)`|返回切片中下标为index的元素的不可变借用|const int *borrow _Nonnull first = slice.get(0);| +|`safe T *borrow _Nonnull SliceMut::get_mut(This this, size_t index)`|返回切片中下标为index的元素的可变借用|int *borrow _Nonnull first = slice.get_mut(0);| +|`safe size_t SliceMut::length(This this)`|返回切片长度|size_t len = slice.length();| +|`safe _Bool SliceMut::is_empty(This this)`|判断切片是否为空|_Bool is_empty = slice.is_empty();| +|`unsafe const T *_Nonnull SliceMut::as_ptr(This this)`|返回切片中保存的裸指针|const T *_Nonnull ptr = slice.as_ptr();| +|`unsafe T *_Nonnull SliceMut::as_mut_ptr(This this)`|返回切片中保存的裸指针|T *_Nonnull ptr = slice.as_mut_ptr();| + #### LinkedList ##### 概述: @@ -6783,6 +6852,7 @@ _direct-declarator : .... _identifier _template-declaration_opt _nested-name-specifier +int main(void) { _direct-declarator _identifier _template-declaration_opt ( _parameter-type-list ) ``` diff --git a/libcbs/src/CMakeLists.txt b/libcbs/src/CMakeLists.txt index 2fbd38579c363e576d7d3ecc412bc8c5a702fedb..43718f71f41910e386ecd4c7852956fe9cdf85a6 100644 --- a/libcbs/src/CMakeLists.txt +++ b/libcbs/src/CMakeLists.txt @@ -16,6 +16,7 @@ target_include_directories(stdcbs raw_vec vec scheduler + slice string list hash @@ -51,6 +52,7 @@ install(FILES raw_table/raw_table.hbs result/result.hbs rc/rc.hbs + slice/slice.hbs cell/cell.hbs DESTINATION include/libcbs ) diff --git a/libcbs/src/slice/slice.hbs b/libcbs/src/slice/slice.hbs new file mode 100644 index 0000000000000000000000000000000000000000..aea06b6798d04d24d264007efd3570b734007767 --- /dev/null +++ b/libcbs/src/slice/slice.hbs @@ -0,0 +1,229 @@ +#ifndef BISHENG_C_SAFETY_SLICE_H +#define BISHENG_C_SAFETY_SLICE_H + +#include "bishengc_safety.hbs" +#include + +/// A read-only slice of a contiguous sequence of type T. +/// +/// Slice provides safe, read-only access to a contiguous sequence of elements +/// without owning the underlying memory. Indeed, slice is a view into a block +/// of memory represented as an immutable borrow pointer and a length. +/// +/// When accessing elements, the slice enforces bounds checking. If an out of +/// bounds access occurs, the program will abort. +/// +/// SAFETY: +/// - The `len` field is assumed to be correct and represents the actual number +/// of elements of type T that can be safely accessed starting from `ptr`. +/// - Given a valid `len`, all slice operations are safe as long as proper +/// bounds checking is performed. +struct Slice { + const T *borrow _Nonnull ptr; + size_t len; +}; + +/// Create a new `Slice` from a pointer and a length. +/// +/// This function constructs a new slice that borrows the memory pointed to by +/// `ptr` and has a length of `len` elements of type `T`. +/// +/// SAFETY: +/// - It's an unsafe function, the caller must ensure that `len` is correct. +unsafe Slice Slice::new(const T *borrow _Nonnull ptr, size_t len) { + Slice slice = { .ptr = ptr, .len = len }; + return slice; +} + +/// Create a new `Slice` representing a sub-slice of the current slice. +/// +/// This function returns a new slice that borrows a portion of the current +/// slice, starting from the `start` index (inclusive) and ending at the `end` +/// index (exclusive). +safe Slice Slice::sub(This this, size_t start, size_t end) { + if (start > end) { + unsafe bsc_out_of_bound_handler(end, start); + } + if (end > this.len) { + unsafe bsc_out_of_bound_handler(this.len, end); + } + unsafe { + const T *raw = (const T *)this.ptr; + raw += start; + return Slice::new((const T *borrow)raw, end - start); + } +} + +/// Get an immutable borrow to the element at the specified index. +/// +/// This function returns an immutable borrow to the element at the specified +/// `index` in the slice. +/// +/// Bounds checking is performed. If an out of bounds access occurs, it aborts. +safe const T *borrow _Nonnull Slice::get(This this, size_t index) { + if (index >= this.len) { + unsafe bsc_out_of_bound_handler(this.len, index); + } + unsafe { + const T *raw = (const T *)this.ptr; + raw += index; + return (const T *borrow)raw; + } +} + +/// Return the length of the slice. +/// +/// This function returns the number of elements in the slice. +safe size_t Slice::length(This this) { + return this.len; +} + +/// Check if the slice is empty. +/// +/// This function returns true if the slice has zero elements, false otherwise. +safe _Bool Slice::is_empty(This this) { + return this.len == 0 ? true : false; +} + +/// Get the underlying raw pointer of the slice. +/// +/// This function returns a const-qualified pointer to the underlying pointer, +/// which allows read-only access to the slice's elements. +unsafe const T *_Nonnull Slice::as_ptr(This this) { + return (const T *_Nonnull)this.ptr; +} + +/// A mutable slice of a contiguous sequence of type T. +/// +/// SliceMut provides safe, mutable access to a contiguous sequence of elements +/// without owning the underlying memory. Indeed, slice is a view into a block +/// of memory represented as an mutable borrow pointer and a length. +/// +/// When accessing elements, the slice enforces bounds checking. If an out of +/// bounds access occurs, the program will abort. +/// +/// SAFETY: +/// - The `len` field is assumed to be correct and represents the actual number +/// of elements of type T that can be safely accessed starting from `ptr`. +/// - Given a valid `len`, all slice operations are safe as long as proper +/// bounds checking is performed. +struct SliceMut { + T *borrow _Nonnull ptr; + size_t len; +}; + +/// Create a new `SliceMut` from a pointer and a length. +/// +/// This function constructs a new mutable slice that borrows the memory pointed to by +/// `ptr` and has a length of `len` elements of type `T`. +/// +/// SAFETY: +/// - It's an unsafe function, the caller must ensure that `len` is correct. +unsafe SliceMut SliceMut::new(T *borrow _Nonnull ptr, size_t len) { + SliceMut slice = { .ptr = ptr, .len = len }; + return slice; +} + +/// Create a new `Slice` representing a sub-slice of the current slice. +/// +/// This function returns a new slice that borrows a portion of the current +/// slice, starting from the `start` index (inclusive) and ending at the `end` +/// index (exclusive). +safe Slice SliceMut::sub(This this, size_t start, size_t end) { + if (start > end) { + unsafe bsc_out_of_bound_handler(end, start); + } + if (end > this.len) { + unsafe bsc_out_of_bound_handler(this.len, end); + } + unsafe { + const T *raw = (const T *)this.ptr; + raw += start; + return Slice::new((const T *borrow)raw, end - start); + } +} + +/// Create a new `SliceMut` representing a sub-slice of the current slice. +/// +/// This function returns a new slice that borrows a portion of the current +/// slice, starting from the `start` index (inclusive) and ending at the `end` +/// index (exclusive). +safe SliceMut SliceMut::sub_mut(This this, size_t start, size_t end) { + if (start > end) { + unsafe bsc_out_of_bound_handler(end, start); + } + if (end > this.len) { + unsafe bsc_out_of_bound_handler(this.len, end); + } + unsafe { + T *raw = (T *)this.ptr; + raw += start; + return SliceMut::new((T *borrow)raw, end - start); + } +} + +/// Get an immutable borrow to the element at the specified index. +/// +/// This function returns an immutable borrow to the element at the specified +/// `index` in the slice. +/// +/// Bounds checking is performed. If an out of bounds access occurs, it aborts. +safe const T *borrow _Nonnull SliceMut::get(This this, size_t index) { + if (index >= this.len) { + unsafe bsc_out_of_bound_handler(this.len, index); + } + unsafe { + const T *raw = (const T *)this.ptr; + raw += index; + return (const T *borrow)raw; + } +} + +/// Get a mutable borrow to the element at the specified index. +/// +/// This function returns a mutable borrow to the element at the specified +/// `index` in the slice. +/// +/// Bounds checking is performed. If an out of bounds access occurs, it aborts. +safe T *borrow _Nonnull SliceMut::get_mut(This this, size_t index) { + if (index >= this.len) { + unsafe bsc_out_of_bound_handler(this.len, index); + } + unsafe { + T *raw = (T *)this.ptr; + raw += index; + return (T *borrow)raw; + } +} + +/// Get the length of the slice. +/// +/// This function returns the number of elements in the slice. +safe size_t SliceMut::length(This this) { + return this.len; +} + +/// Check if the mutable slice is empty. +/// +/// This function returns true if the slice has zero elements, false otherwise. +safe _Bool SliceMut::is_empty(This this) { + return this.len == 0 ? true : false; +} + +/// Get the underlying raw pointer of the slice. +/// +/// This function returns a const-qualified pointer to the underlying pointer, +/// which allows read-only access to the slice's elements. +unsafe const T *_Nonnull SliceMut::as_ptr(This this) { + return (const T *_Nonnull)this.ptr; +} + +/// Get the underlying mutable raw pointer of the slice. +/// +/// This function returns a mutable pointer to the underlying pointer, +/// which allows both read and write access to the slice's elements. +unsafe T *_Nonnull SliceMut::as_mut_ptr(This this) { + return (T *_Nonnull)this.ptr; +} + +#endif diff --git a/libcbs/test/CMakeLists.txt b/libcbs/test/CMakeLists.txt index 918c75ecb28a526aecf68c01eee6511318714de6..402a6d5b140662039ae3ba4d67aba94141acb197 100644 --- a/libcbs/test/CMakeLists.txt +++ b/libcbs/test/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(bishengc_safety) add_subdirectory(list) add_subdirectory(map) add_subdirectory(scheduler) +add_subdirectory(slice) add_subdirectory(string) add_subdirectory(vec) add_subdirectory(rc) diff --git a/libcbs/test/slice/CMakeLists.txt b/libcbs/test/slice/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6dbd2b14a20c6fb5bc93870fd86d3a7243a23154 --- /dev/null +++ b/libcbs/test/slice/CMakeLists.txt @@ -0,0 +1,9 @@ +add_libcbs_test_suite(libcbs-slice-tests) + +add_libcbs_test( + slice_test + SUITE + libcbs-slice-tests + SRCS + slice_test.cbs +) diff --git a/libcbs/test/slice/slice_test.cbs b/libcbs/test/slice/slice_test.cbs new file mode 100644 index 0000000000000000000000000000000000000000..6ac5674fe5ece5e9d5de4919da56fe4ea5b03c31 --- /dev/null +++ b/libcbs/test/slice/slice_test.cbs @@ -0,0 +1,134 @@ +// Always enable asserts. +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include "slice.hbs" +#include +#include +#include + +void test1() { + int arr[5] = {1, 2, 3, 4, 5}; + Slice slice = Slice::new(&const *arr, 5); + const int *borrow _Nonnull first = slice.get(0); + assert(*first == 1); +} + +void test2() { + int arr[5] = {1, 2, 3, 4, 5}; + Slice slice = Slice::new(&const arr[1], 3); + const int *borrow _Nonnull first = slice.get(0); + assert(*first == 2); + Slice slice_copy = slice; + assert(slice_copy.length() == 3); + const int *borrow _Nonnull second = slice.get(1); + assert(*second == 3); + assert(*slice_copy.get(2) == 4); +} + +void test3() { + int *p = (int *)malloc(sizeof(int) * 10); + if (!p) return; + memset(p, 0, sizeof(int) * 10); + p[8] = 1; + p[9] = 10; + Slice slice = Slice::new(&const p[8], 2); + assert(*slice.get(0) == 1); + assert(*slice.get(1) == 10); + assert(slice.length() == 2); + free(p); +} + +void test4() { + int *p = (int *)malloc(sizeof(int) * 10); + if (!p) return; + memset(p, 0, sizeof(int) * 10); + p[4] = 2; + p[5] = 3; + p[7] = 3; + Slice slice = Slice::new(&const p[4], 4); + assert(*slice.get(0) == 2); + assert(slice.length() == 4); + assert(slice.is_empty() == false); + assert(slice.as_ptr() == &p[4]); + Slice sub_slice = slice.sub(1, 3); + assert(sub_slice.length() == 2); + assert(*sub_slice.get(0) == 3); + assert(slice.get(1) == sub_slice.get(0)); +} + +safe void test5(void) { + int arr[5] = {1, 2, 3, 4, 5}; + unsafe Slice slice = Slice::new(&const arr[0], 10); + unsafe assert(*slice.get(4) == 5); + unsafe const int *p = slice.as_ptr(); + unsafe assert(p[2] == 3); +} + +void test6() { + int arr[5] = {1, 2, 3, 4, 5}; + SliceMut slice = SliceMut::new(&mut *arr, 5); + assert(slice.is_empty() == false); + assert(slice.length() == 5); + assert(*slice.get(4) == 5); + *slice.get_mut(4) = 10; + assert(*slice.get(4) == 10); +} + +void test7() { + char arr[] = "hello world"; + char *first = &arr[5]; + char last = arr[11]; + SliceMut slice = SliceMut::new(&mut arr[5], 7); + assert(slice.is_empty() == false); + assert(slice.length() == 7); + assert(*slice.get(6) == last); + assert(*slice.get(6) == '\0'); + *slice.get_mut(0) = ','; + assert(*first == ','); + const char *_Nonnull ptr = slice.as_ptr(); + assert(ptr[3] == 'r'); +} + +safe void test8(void) { + char arr[] = "hello, bisheng c"; + unsafe SliceMut slice = SliceMut::new(&mut *arr, 17); + unsafe assert(slice.is_empty() == false); + unsafe assert(slice.length() == 17); + unsafe char *ptr_mut = slice.as_mut_ptr(); + unsafe ptr_mut[7] = 'B'; + unsafe assert(*slice.get(7) == 'B'); + *slice.get_mut(9) = 'S'; + unsafe assert(ptr_mut[9] == 'S'); +} + +void test9() { + int arr[5] = {1, 2, 3, 4, 5}; + SliceMut slice = SliceMut::new(&mut *arr, 5); + Slice sub = slice.sub(0, 4); + assert(sub.is_empty() == false); + assert(sub.length() == 4); + assert(*sub.get(3) == 4); + assert(slice.length() == 5); + SliceMut sub_mut = slice.sub_mut(0, 4); + assert(sub_mut.is_empty() == false); + assert(sub_mut.length() == 4); + assert(*sub_mut.get(3) == 4); + *sub_mut.get_mut(3) = 5; + assert(slice.length() == 5); + assert(arr[4] == 5); +} + +int main(void) { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); + return 0; +}