contributed by < ccs100203
>
linux2021
仿效 folly::fbstring,提出 C 語言的實作:
看出是以 16 byte 設為一個 string 的大小,
短字串為長度小於 16 的字串,並存放在 stack。
中字串在 16 ~ 256 之間,並利用動態記憶體配置在 heap。
長字串為長度大於 256 的字串,一樣存放在 heap,但多了 CoW 的機制。
LARGE_STRING_LEN
定義了中字串跟長字串的長度閥值。
透過 is_ptr
flag,判斷 string 是否透過 pointer 儲存在 heap 內。
透過 is_large_string
flag,判斷 string 是否為 large string。
此函式回傳 string 的大小,透過 xs_is_ptr
判斷 string 的儲存位置,再利用不同方式取得長度。
space_left
儲存的是 string 所剩下的長度,所以透過總長度 15 - space_left
就可以得到 string 的當前長度。size
之中,直接調用即可。此函數用來取得 string 所儲存的資料,透過 xs_is_ptr
判斷 string 的儲存位置。
x->data
x->ptr
。用來取得 string 的當前最大容量,透過 xs_is_ptr
判斷 string 的儲存位置。
x->capacity
作為 shift 的數量,表達 2 的冪的效果。#define MAX_STR_LEN ((1UL << MAX_STR_LEN_BITS) - 1)
有關取用次數的相關實作,大部分都會先判斷字串是否屬於長字串才做操作,做了一層保護。
reallocate
的 flag,用來判斷是否需要藉由 realloc
重新配置空間。LARGE_STRING_LEN
作為分辨的依據。LARGE_STRING_LEN
的字串會在最前面做 4 byte 的 padding,將其保留給 reference count 使用,這也是為什麼 xs_data
在使用長字串時需要先做 此函式用來建立一個新的 xs string。
由字串的長度決定要如何儲存他,小於 16 時會將他存放在 stack。
中字串跟長字串則是藉由上面提到的 xs_allocate_data
去配置大小。
xs_literal_empty
用來協助宣告,並且將字串的空間初始化為 0。
xs_tmp
此程式其實不直接呼叫 xs_new
,而都是用此 macro 包裝,做了一層判斷字串長度是否超出最大長度限制的判斷及保護。
_Static_assert
根據 c11 6.7.10 Static assertions
_Static_assert ( constant-expression , string-literal ) ;
The constant expression shall be an integer constant expression. If the value of the constant expression compares unequal to 0, the declaration has no effect. Otherwise, the constraint is violated and the implementation shall produce a diagnostic message that includes the text of the string literal, except that characters not in the basic source character set are not required to appear in the message.
如果 expression 不為 0 的話無任何作用,反之會告知 string-literal 內的訊息。
首先知道 xs_tmp
是為了 xs_new
所作的一層封裝,其作用在新增新的空間時,會提前檢查字串大小是否超出 MAX_STR_LEN
的範圍。
上面的第 14 行是程式中使用 xs_tmp
的情形,因為需要確保進行檢查的過程是不會影響到原先函式的呼叫方式與運作,所以這邊使用了 comma expression 的技巧。
根據 c11 6.5.17 Comma operator
The left operand of a comma operator is evaluated as a void expression; there is a
sequence point between its evaluation and that of the right operand. Then the right
operand is evaluated; the result has its type and value.
可以了解 comma 左邊的 expression 會先處理,然後再處理以及回傳右邊的 expression,這樣可以確保在進行 xs_new
之前會先通過 _Static_assert
的檢查,並且 _Static_assert
不會回傳所以不影響到原先的使用方式。
但是如果只在 comma 左邊放一個 _Static_assert()
會遇到問題,因為 _Static_assert
本身是一個 macro 並不是 expression,所以他不符合 comma expression 所規範的 syntax。
所以程式透過把 _Static_assert
包裝在一個 structure 內,並利用 compound literal 的方式將其轉變為一個 expression。此時的程式就可以保有在 macro 內進行 _Static_assert
檢查且不影響其原先使用方式的效果。 (參考 c11 6.5.2.5 Compound literals,其歸類在 Postfix operators 之中)
使用 (void) 的部份還尚未釐清,有參考同學的共筆做實驗,但不論我有沒有加入 (void) 都不會在編譯時產生 warning,所以還搞不懂原因。
此函式的作用在結合上述的 xs_allocate_data
重新動態配置新的空間給 xs string。
xs_allocate_data
的地方會根據當前資料的存放位置有所不同
reallocate
設為 1,藉此調用函式中的 realloc
而不是重新配置一整塊空間。這段程式碼原先有寫錯的地方,第 12 行會導致後面的 else 根本不會進入,勢必需要改寫,所以做了以下分析:
綜合以上的分析,將程式碼改為下列這樣:
第 20 行
x->capacity = ilog2(len) + 1;
移至第 12 行if (xs_is_ptr(x))
前,因為在xs_allocate_data
函式中,會根據x->capacity
配置記憶體,所以如果沒將x->capacity
更新的話,配置的記憶體空間會和原本相同。
Chia-Lun Hsu <gyes00205>
感謝,之前沒注意到。
Shao-Tse Hung <ccs100203>
TODO 增加長度超過 MAX_STR_LEN
之後的判斷,但還在思考好的寫法,也還沒搞懂 xs_tmp 中的寫法。
xs_literal_empty
會將其重新指向一塊空的結構體xs_free
xs_dec_refcnt
對 reference count 減 1,再決定是否要 free 掉。xs_allocate_data
配置新的空間後,將原先的指標 data
指向新配出的空間。此函式的作用是對於給定的參數進行字串串接。
memmove
移動原先的 string,空出 prefix 所需要的空間memcpy
將需要串接的資料放入預計接上的位置。xs_grow
配置出串接後的字串所需要的空間tmps
內tmps
memmove
移動到空出 prefix 的位置,最後在將資料接上。memmove
將 src
內 n
bytes 的資料按順序從第一個開始,先放入 buffer 再複製到 dest
,不斷進行到移動結束。此函式可以接受 memory area overlap。
memcpy
將資料從 src
開始,複製 n
bytes 到 dest
。此函式不可接受 memory area overlap。
將字串 concat 到超過 MAX_STR_LEN
時沒有做偵測,我認為需要在 xs_grow
中設立判斷機制。
此函式的用處在於修剪掉字串頭尾的指定字元。(註解寫的內容尚未能搞懂)
memmove
將剩下的資料重新放到指標的一開始,並確保字串的結尾是 \0
解釋 check_bit 及 set_bit 的運作原理:
mask[(uint8_t) byte / 8]
以及 1 << (uint8_t) byte % 8
作為 index,用來索引當前字元所對應到的 bit。&
即可判斷該字元是否同時存在於 trimset 與 string 之中。而位元運算子 or |
則可將需要刪減掉的字元新增進去 trimset
中。TODO