Skip to content

Commit c59dd80

Browse files
code5717cursoragent
andcommitted
Implement checked array.h API and align docs with the site.
Replace the prototype header with overflow-safe growth, try_* accessors, and strict-C portability, then document the contracts and add compile-proof tests so doc snippets stay verified. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent fa5fa2b commit c59dd80

33 files changed

Lines changed: 897 additions & 127 deletions

README.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ Use checked macros by default. Keep unchecked macros only for compatibility or e
1919
- `array_make(T, size)`: allocate array with initial capacity (`size` can be `0`).
2020
- `array_free(arr)`: free array memory.
2121
- `array_reserve(arr, min_capacity)`: ensure capacity, returns `bool` and may update `arr` after `realloc`.
22-
- `array_try_push(arr, value)`: append one value, returns `bool`.
23-
- `array_try_at(arr, idx, out_ptr)`: bounds-checked access, returns `bool`.
22+
- `array_try_push(arr, value)`: append scalar rvalues, returns `bool`.
23+
- `array_try_push_lvalue(arr, value)`: append struct or other non-scalar elements, returns `bool`.
24+
- `array_try_at(arr, idx, out_ptr)`: bounds-checked access; writes a pointer into the array, returns `bool`.
2425
- `array_try_slice_t(T, arr, low, high, out_slice)`: bounds-checked slice creation, returns `bool`.
2526
- `array_back_ptr(arr)`: pointer to last element or `NULL` when empty.
2627
- `array_length(arr)`: element count.
@@ -41,8 +42,16 @@ These are kept for compatibility and speed-focused code paths:
4142
4243
## Portability notes
4344
44-
- Strict C11/C17 path: use `array_for_each_t(T, arr, it)` and typed slice macros.
45-
- GNU/Clang convenience path: if `typeof` is supported, `array_for_each(arr, it)` is available.
45+
- Strict C11/C17 path (`-std=c11 -pedantic`): `ARRAY_HAS_TYPEOF` is `0`; use `array_for_each_t(T, arr, it)` and typed slice macros.
46+
- GNU/Clang convenience path: `ARRAY_HAS_TYPEOF` is `1`; `array_for_each(arr, it)` and `slice_from_array` are available.
47+
48+
## Compile verification
49+
50+
Doc snippets compile under strict C11:
51+
52+
```sh
53+
tests/compile/run.sh
54+
```
4655

4756
## Example (safe usage)
4857

@@ -117,4 +126,4 @@ npm run build
117126
- Workflow file: `.github/workflows/pages.yml`
118127
- Trigger: push to `main` (or manual `workflow_dispatch`)
119128
- Output: `site/dist` uploaded as GitHub Pages artifact
120-
- Expected URL after deploy: `https://airbus5717.github.io/arraylist/`
129+
- Expected URL after deploy: `https://code5717.github.io/Arraylist/`

array.h

Lines changed: 309 additions & 53 deletions
Large diffs are not rendered by default.

docs/api-reference.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,17 @@ This reference is organized by operation group and focuses on behavior contracts
4343
### `array_try_push(arr, value) -> bool`
4444

4545
- Purpose: append one element at the logical end.
46-
- Preconditions: `arr` non-`NULL`.
46+
- Preconditions: `arr` non-`NULL`; `value` must be a scalar type covered by the macro's `_Generic` dispatch (integers and floating-point).
4747
- Failure behavior: returns `false` if `arr` is null, count overflows, or growth fails.
4848
- Complexity: amortized `O(1)`, worst-case `O(n)` on resize.
49+
- Notes: copies the value into the array. Literal and rvalue scalars (for example `10`, loop index) are supported.
50+
51+
### `array_try_push_lvalue(arr, value) -> bool`
52+
53+
- Purpose: append one element when `T` is a struct or other non-scalar type, or when pushing through an lvalue is clearer.
54+
- Preconditions: `arr` non-`NULL`; `value` must be an lvalue of the array's element type.
55+
- Failure behavior: same as `array_try_push`.
56+
- Complexity: same as `array_try_push`.
4957

5058
### `array_push(arr, value)` (compatibility)
5159

@@ -58,13 +66,14 @@ This reference is organized by operation group and focuses on behavior contracts
5866

5967
### `array_try_at(arr, idx, out_ptr) -> bool`
6068

61-
- Purpose: checked element access by index.
69+
- Purpose: checked element access by index; writes a pointer to the element inside the array (not a copy).
6270
- Preconditions:
6371
- `arr` non-`NULL`
6472
- `idx < arr->count`
65-
- `out_ptr` non-`NULL`
66-
- Failure behavior: returns `false` when any precondition fails.
73+
- `out_ptr` non-`NULL` (address of a `T*` variable)
74+
- Failure behavior: returns `false` when any precondition fails; does not write `*out_ptr` on failure.
6775
- Complexity: `O(1)`.
76+
- Notes: the returned pointer is invalidated if the array is reallocated or freed.
6877

6978
### `array_at(arr, idx)` (unchecked)
7079

@@ -123,10 +132,17 @@ This reference is organized by operation group and focuses on behavior contracts
123132
### `array_for_each(arr, it)` (GNU/Clang convenience)
124133

125134
- Purpose: inferred-type iteration where `typeof` is available.
126-
- Preconditions: `ARRAY_HAS_TYPEOF` enabled and `arr` non-`NULL`.
127-
- Failure behavior: unavailable in strict mode; otherwise same preconditions as typed iteration.
135+
- Preconditions: `ARRAY_HAS_TYPEOF` is `1` (GNU/Clang, not strict ISO C) and `arr` non-`NULL`.
136+
- Failure behavior: macro is unavailable when `ARRAY_HAS_TYPEOF` is `0`; otherwise same preconditions as typed iteration.
128137
- Complexity: `O(n)`.
129138

139+
### `slice_from_array(arr, low, high)` (GNU/Clang convenience)
140+
141+
- Purpose: unchecked slice creation with inferred element type.
142+
- Preconditions: `ARRAY_HAS_TYPEOF` is `1`; bounds valid.
143+
- Failure behavior: unavailable in strict mode; undefined behavior if bounds are invalid.
144+
- Complexity: `O(1)`.
145+
130146
## Metadata and nullable helpers
131147

132148
### `array_length(arr)` / `array_is_empty(arr)`

docs/examples.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,35 @@ array_for_each(arr, it)
9191

9292
Why this matters: same runtime behavior as typed iteration, but shorter syntax when `typeof` is available.
9393

94-
## 8) Cleanup pattern
94+
## 8) Struct element push (non-scalar)
95+
96+
```c
97+
typedef struct { int x; int y; } Point;
98+
generate_array_type(Point);
99+
100+
Array(Point) points = array_make(Point, 0);
101+
Point p = { .x = 1, .y = 2 };
102+
103+
if (!array_try_push_lvalue(points, p))
104+
{
105+
array_free(points);
106+
return 1;
107+
}
108+
```
109+
110+
Why this matters: `array_try_push` dispatches scalar types with `_Generic`; struct elements use the lvalue helper.
111+
112+
## 9) Cleanup pattern
95113
96114
```c
97115
array_free(arr);
98116
arr = NULL;
99117
```
100118

101119
Why this matters: freeing ownership plus nulling local pointers helps prevent accidental reuse.
120+
121+
Compile-check all examples:
122+
123+
```sh
124+
tests/compile/run.sh
125+
```

docs/overview.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ Unchecked compatibility APIs:
7878
- Assume preconditions are already true.
7979
- Are concise, but undefined behavior is possible if used incorrectly (for example, out-of-bounds access or last-element access on empty arrays).
8080

81+
## Portability
82+
83+
- `array_size_t` is a `size_t` alias used for counts, capacities, and slice lengths.
84+
- Strict C11/C17 (`-std=c11 -pedantic`): use `array_for_each_t(T, arr, it)` and typed slice macros. `ARRAY_HAS_TYPEOF` is `0`.
85+
- GNU/Clang convenience (`-std=gnu11` or non-pedantic GCC/Clang): `ARRAY_HAS_TYPEOF` is `1`, enabling `array_for_each` and `slice_from_array`.
86+
- `array_try_push` accepts scalar rvalues (for example `array_try_push(arr, 10)`) via C11 `_Generic`. For struct or other non-scalar element types, use `array_try_push_lvalue(arr, value)` with an lvalue.
87+
8188
## Complexity at a glance
8289

8390
| Operation | Complexity | Notes |

docs/quickstart.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,24 @@ What this demonstrates:
6161

6262
## 3) Compile commands
6363

64-
Strict C11/C17 path:
64+
Strict C11/C17 path (`ARRAY_HAS_TYPEOF` is `0`; use `array_for_each_t`):
6565

6666
```sh
6767
cc -std=c11 -Wall -Wextra -pedantic demo.c -o demo
6868
```
6969

70-
GNU11 convenience path (`array_for_each` available when `typeof` is supported):
70+
GNU11 convenience path (`ARRAY_HAS_TYPEOF` is `1`; `array_for_each` available):
7171

7272
```sh
7373
cc -std=gnu11 -Wall -Wextra demo.c -o demo
7474
```
7575

76+
Verify the doc snippets in this repository:
77+
78+
```sh
79+
tests/compile/run.sh
80+
```
81+
7682
## 4) Failure-handling pattern (recommended)
7783

7884
For mutating operations, check every `bool`-returning macro:
@@ -97,7 +103,9 @@ Why this pattern works:
97103
- Confirm you are using checked APIs and handling return values.
98104
- Iterator macro missing:
99105
- Use `array_for_each_t` for strict C mode; `array_for_each` requires `ARRAY_HAS_TYPEOF`.
106+
- Push fails on struct element types:
107+
- Use `array_try_push_lvalue(arr, value)` instead of `array_try_push` for struct or other non-scalar `T`.
100108
- Pointer issues after growth:
101-
- Remember reallocation can move the array block; stale element pointers may become invalid.
109+
- Remember reallocation can move the array block; pointers from `array_try_at` or slices may become invalid.
102110

103111
Next: [API Reference](./api-reference.md).

site/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
content="Header-only dynamic array macros for C with checked APIs, explicit ownership, and strict-C portability."
1616
/>
1717
<meta property="og:type" content="website" />
18-
<meta property="og:url" content="https://airbus5717.github.io/arraylist/" />
18+
<meta property="og:url" content="https://code5717.github.io/Arraylist/" />
1919
<meta name="theme-color" content="#0d1117" />
2020
</head>
2121
<body>

site/package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
},
2323
"devDependencies": {
2424
"@eslint/js": "^9.39.1",
25+
"@types/mdast": "^4.0.4",
2526
"@types/node": "^24.10.1",
2627
"@types/react": "^19.2.7",
2728
"@types/react-dom": "^19.2.3",
@@ -35,6 +36,7 @@
3536
"tailwindcss": "^3.4.19",
3637
"typescript": "~5.9.3",
3738
"typescript-eslint": "^8.48.0",
39+
"unist-util-visit": "^5.1.0",
3840
"vite": "^7.3.1"
3941
}
4042
}

site/src/components/CodeBlock.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useRef, useState } from 'react'
1+
import { useEffect, useRef, useState } from 'react'
22
import type { ReactNode } from 'react'
33

44
type CodeBlockProps = {
@@ -8,18 +8,34 @@ type CodeBlockProps = {
88
export function CodeBlock({ children }: CodeBlockProps) {
99
const preRef = useRef<HTMLPreElement>(null)
1010
const [copied, setCopied] = useState(false)
11+
const timeoutRef = useRef<number | null>(null)
1112
const canCopy = typeof navigator !== 'undefined' && Boolean(navigator.clipboard)
1213

14+
useEffect(() => {
15+
return () => {
16+
if (timeoutRef.current !== null) {
17+
window.clearTimeout(timeoutRef.current)
18+
}
19+
}
20+
}, [])
21+
1322
async function handleCopy() {
1423
const text = preRef.current?.textContent ?? ''
1524

1625
if (!text || !navigator.clipboard) {
1726
return
1827
}
1928

20-
await navigator.clipboard.writeText(text)
21-
setCopied(true)
22-
window.setTimeout(() => setCopied(false), 2000)
29+
try {
30+
await navigator.clipboard.writeText(text)
31+
setCopied(true)
32+
if (timeoutRef.current !== null) {
33+
window.clearTimeout(timeoutRef.current)
34+
}
35+
timeoutRef.current = window.setTimeout(() => setCopied(false), 2000)
36+
} catch {
37+
setCopied(false)
38+
}
2339
}
2440

2541
return (

0 commit comments

Comments
 (0)