Skip to content

Commit b6978dc

Browse files
committed
DOCS: README 수정 및 FRONT,BACK -> HEAD,TAIL로 변경
1 parent 6d79839 commit b6978dc

7 files changed

Lines changed: 170 additions & 110 deletions

File tree

README.md

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Zero dependencies (**React optional**) — perfect for logs, streaming data, rol
77

88
## Features
99

10-
- 🔄 **CircularBuffer (Low-level)** — direction-based circular buffer primitive (FRONT/BACK)
10+
- 🔄 **CircularBuffer (Low-level)** — direction-based circular buffer primitive (HEAD/TAIL)
1111
- 📦 **BufferManager (High-level)** — convenient API (push/pop single or arrays, peek helpers, utilities)
1212
- ⚛️ **React Hook**`useCircularBuffer` for automatic re-rendering in React
1313
- 🎯 **Type-Safe** — full TypeScript generics support
@@ -48,26 +48,26 @@ import { CircularBuffer, Direction } from "circular-queue-react";
4848

4949
const buf = new CircularBuffer<string>(3);
5050

51-
// Push to BACK (newest side)
52-
buf.push("A", Direction.BACK);
53-
buf.push("B", Direction.BACK);
51+
// Push to TAIL (newest side)
52+
buf.push("A", Direction.TAIL);
53+
buf.push("B", Direction.TAIL);
5454

55-
// Push to FRONT (oldest side)
56-
buf.push("Z", Direction.FRONT);
55+
// Push to HEAD (oldest side)
56+
buf.push("Z", Direction.HEAD);
5757

58-
console.log(buf.get(Direction.FRONT)); // "Z" (oldest)
59-
console.log(buf.get(Direction.BACK)); // "B" (newest)
58+
console.log(buf.get(Direction.HEAD)); // "Z" (oldest)
59+
console.log(buf.get(Direction.TAIL)); // "B" (newest)
6060

6161
// Peek many
62-
console.log(buf.get(Direction.FRONT, 2)); // ["Z","A"] (oldest -> newer)
63-
console.log(buf.get(Direction.BACK, 2)); // ["B","A"] (newest -> older)
62+
console.log(buf.get(Direction.HEAD, 2)); // ["Z","A"] (oldest -> newer)
63+
console.log(buf.get(Direction.TAIL, 2)); // ["B","A"] (newest -> older)
6464

6565
// Iterate (oldest -> newest)
6666
for (const x of buf) console.log(x);
6767

6868
// Pop
69-
console.log(buf.pop(Direction.FRONT)); // removes oldest ("Z")
70-
console.log(buf.pop(Direction.BACK)); // removes newest ("B")
69+
console.log(buf.pop(Direction.HEAD)); // removes oldest ("Z")
70+
console.log(buf.pop(Direction.TAIL)); // removes newest ("B")
7171

7272
// Resize (logical capacity)
7373
buf.resize(10);
@@ -90,11 +90,11 @@ import { BufferManager } from "circular-queue-react";
9090

9191
const b = new BufferManager<string>(3);
9292

93-
// pushTail keeps newest at the end (BACK)
93+
// pushTail keeps newest at the end (TAIL)
9494
b.pushTail(["A", "B", "C", "D"]);
9595
console.log(b.getAll()); // ["B","C","D"] (keeps last 3)
9696

97-
// pushHead inserts at FRONT, preserves input order
97+
// pushHead inserts at HEAD, preserves input order
9898
b.pushHead(["X", "Y"]);
9999
console.log(b.getAll()); // ["X","Y","B"] (oldest -> newest)
100100

@@ -124,7 +124,7 @@ buf.pushTail([1, 2, 3, 4]); // keeps [2,3,4]
124124

125125
### 4) React Hook
126126

127-
`useCircularBuffer` provides a BufferManager-backed stateful hook:
127+
`useCircularBuffer` provides a BufferManager-TAILed stateful hook:
128128

129129
- data auto-updates after mutations
130130
- `pushHead` / `pushTail` / `popHead` / `popTail` / `replaceAll` / `clear` / `resize`
@@ -178,13 +178,72 @@ This library uses consistent ordering rules:
178178

179179
---
180180

181+
## Important Type Limitation
182+
183+
**⚠️ When `T` itself is an array type, the array overload for `pushHead`/`pushTail` cannot be used.**
184+
185+
### Why?
186+
187+
TypeScript cannot distinguish between:
188+
- `T` (when `T = number[]`)
189+
- `readonly T[]` (which would be `readonly number[][]`)
190+
191+
Both resolve to array types, making overload resolution ambiguous.
192+
193+
### Example
194+
195+
```ts
196+
// ❌ PROBLEMATIC: T = number[]
197+
const buf = new BufferManager<number[]>(5);
198+
199+
// This will fail! TypeScript cannot tell if you mean:
200+
// 1. Push a single item (which happens to be an array): number[]
201+
// 2. Push multiple items: readonly number[][]
202+
buf.pushTail([[1, 2], [3, 4]]);
203+
204+
// ✅ SOLUTION: Push one item at a time
205+
buf.pushTail([1, 2]); // Push single array
206+
buf.pushTail([3, 4]); // Push another single array
207+
```
208+
209+
### Workaround
210+
211+
When `T` is an array type, **always push items one at a time** instead of using the array overload:
212+
213+
```ts
214+
const items: number[][] = [[1, 2], [3, 4], [5, 6]];
215+
216+
// ❌ Don't do this:
217+
// buf.pushTail(items);
218+
219+
// ✅ Do this instead:
220+
for (const item of items) {
221+
buf.pushTail(item);
222+
}
223+
224+
// Or use a wrapper type:
225+
type Item = { data: number[] };
226+
const typedBuf = new BufferManager<Item>(5);
227+
typedBuf.pushTail([
228+
{ data: [1, 2] },
229+
{ data: [3, 4] }
230+
]); // ✅ Works!
231+
```
232+
233+
This limitation applies to:
234+
- `BufferManager.pushHead()`
235+
- `BufferManager.pushTail()`
236+
- `useCircularBuffer` hook's `pushHead` and `pushTail`
237+
238+
---
239+
181240
## API Reference
182241

183242
### Direction
184243

185244
```ts
186-
Direction.FRONT; // head / oldest side
187-
Direction.BACK; // tail / newest side
245+
Direction.HEAD; // head / oldest side
246+
Direction.TAIL; // tail / newest side
188247
```
189248

190249
### CircularBuffer`<T>`
@@ -198,7 +257,7 @@ Direction.BACK; // tail / newest side
198257
- `push(item: T, direction: Direction): void`
199258
- `pop(direction: Direction): T | undefined`
200259
- `get(direction: Direction): T | undefined`
201-
- `get(direction: Direction, count: number): T[]` FRONT count: oldest → newer, BACK count: newest → older
260+
- `get(direction: Direction, count: number): T[]` HEAD count: oldest → newer, TAIL count: newest → older
202261

203262
- `clear(): void`
204263
- `resize(newCapacity: number): void` (logical capacity)

examples/basic-usage.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,52 +23,52 @@ type LogEntry = {
2323

2424
const logBuffer = new CircularBuffer<LogEntry>(5); // Small capacity for demo
2525

26-
// Add some logs using push with Direction.BACK (newest side)
26+
// Add some logs using push with Direction.TAIL (newest side)
2727
logBuffer.push(
2828
{ timestamp: Date.now(), level: "info", message: "Application started" },
29-
Direction.BACK
29+
Direction.TAIL
3030
);
3131
logBuffer.push(
3232
{ timestamp: Date.now(), level: "info", message: "Loading configuration" },
33-
Direction.BACK
33+
Direction.TAIL
3434
);
3535
logBuffer.push(
3636
{ timestamp: Date.now(), level: "warn", message: "Cache is full" },
37-
Direction.BACK
37+
Direction.TAIL
3838
);
3939
logBuffer.push(
4040
{ timestamp: Date.now(), level: "error", message: "Connection failed" },
41-
Direction.BACK
41+
Direction.TAIL
4242
);
4343

4444
console.log("Total logs:", logBuffer.getSize());
45-
console.log("Oldest (FRONT):", logBuffer.get(Direction.FRONT));
46-
console.log("Newest (BACK):", logBuffer.get(Direction.BACK));
45+
console.log("Oldest (HEAD):", logBuffer.get(Direction.HEAD));
46+
console.log("Newest (TAIL):", logBuffer.get(Direction.TAIL));
4747

4848
// Peek multiple without removing
4949
console.log(
50-
"Peek newest 2 (BACK, newest->older):",
51-
logBuffer.get(Direction.BACK, 2)
50+
"Peek newest 2 (TAIL, newest->older):",
51+
logBuffer.get(Direction.TAIL, 2)
5252
);
5353

5454
// Add more logs (will overflow)
5555
logBuffer.push(
5656
{ timestamp: Date.now(), level: "info", message: "Retrying connection" },
57-
Direction.BACK
57+
Direction.TAIL
5858
);
5959
logBuffer.push(
6060
{ timestamp: Date.now(), level: "info", message: "Connection successful" },
61-
Direction.BACK
61+
Direction.TAIL
6262
);
6363

6464
console.log("\nAfter overflow:");
6565
console.log("Total logs (still 5):", logBuffer.getSize());
66-
console.log("Oldest (FRONT):", logBuffer.get(Direction.FRONT));
66+
console.log("Oldest (HEAD):", logBuffer.get(Direction.HEAD));
6767
console.log("All logs (oldest->newest):", Array.from(logBuffer));
6868

6969
// Remove one item using pop
70-
const removed = logBuffer.pop(Direction.FRONT);
71-
console.log("\nRemoved from FRONT:", removed);
70+
const removed = logBuffer.pop(Direction.HEAD);
71+
console.log("\nRemoved from HEAD:", removed);
7272
console.log("Size after pop:", logBuffer.getSize());
7373

7474
// ============================================================================
@@ -80,14 +80,14 @@ console.log("==========================================");
8080

8181
const messageBuffer = new BufferManager<string>(10);
8282

83-
// Add to tail (BACK)
83+
// Add to tail (TAIL)
8484
messageBuffer.pushTail("Message 1");
8585
messageBuffer.pushTail("Message 2");
8686
messageBuffer.pushTail("Message 3");
8787

8888
console.log("Messages:", messageBuffer.getAll());
8989

90-
// Add to head (FRONT)
90+
// Add to head (HEAD)
9191
messageBuffer.pushHead("Priority Message!");
9292
console.log("After adding to head:", messageBuffer.getAll());
9393

package-lock.json

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

src/core/BufferManager.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Direction, type IBuffer } from "../types";
66
*
77
* Provides:
88
* - Convenient APIs for pushing/popping single items or arrays
9-
* - Direction-specific helpers (head/front vs tail/back)
9+
* - Direction-specific helpers (head/HEAD vs tail/TAIL)
1010
* - Iterable utilities (forEach/map/filter)
1111
*
1212
* Great for:
@@ -33,7 +33,7 @@ export class BufferManager<T> implements IBuffer<T> {
3333
// ============================================================================
3434

3535
/**
36-
* Push item(s) to the head (FRONT / oldest side).
36+
* Push item(s) to the head (HEAD / oldest side).
3737
*
3838
* When pushing an array, the original order is preserved:
3939
* `pushHead([a,b,c])` results in `a` being the oldest among the inserted items.
@@ -45,7 +45,7 @@ export class BufferManager<T> implements IBuffer<T> {
4545
pushHead(input: readonly T[]): void;
4646
pushHead(input: T | readonly T[]): void {
4747
if (!this.isMany(input)) {
48-
this.buffer.push(input, Direction.FRONT);
48+
this.buffer.push(input, Direction.HEAD);
4949
return;
5050
}
5151

@@ -54,12 +54,12 @@ export class BufferManager<T> implements IBuffer<T> {
5454
input.length > capacity ? input.slice(0, capacity) : input;
5555

5656
for (let i = itemsToAdd.length - 1; i >= 0; i--) {
57-
this.buffer.push(itemsToAdd[i], Direction.FRONT);
57+
this.buffer.push(itemsToAdd[i], Direction.HEAD);
5858
}
5959
}
6060

6161
/**
62-
* Push item(s) to the tail (BACK / newest side).
62+
* Push item(s) to the tail (TAIL / newest side).
6363
*
6464
* Optimization:
6565
* - If input array length exceeds logical capacity, only the last `capacity` items are processed.
@@ -68,15 +68,15 @@ export class BufferManager<T> implements IBuffer<T> {
6868
pushTail(input: readonly T[]): void;
6969
pushTail(input: T | readonly T[]): void {
7070
if (!this.isMany(input)) {
71-
this.buffer.push(input, Direction.BACK);
71+
this.buffer.push(input, Direction.TAIL);
7272
return;
7373
}
7474

7575
const capacity = this.buffer.getLogicalCapacity();
7676
const itemsToAdd = input.length > capacity ? input.slice(-capacity) : input;
7777

7878
for (const item of itemsToAdd) {
79-
this.buffer.push(item, Direction.BACK);
79+
this.buffer.push(item, Direction.TAIL);
8080
}
8181
}
8282

@@ -94,7 +94,7 @@ export class BufferManager<T> implements IBuffer<T> {
9494
popHead(count: number): T[];
9595
popHead(count?: number): T | undefined | T[] {
9696
if (count === undefined) {
97-
return this.buffer.pop(Direction.FRONT);
97+
return this.buffer.pop(Direction.HEAD);
9898
}
9999

100100
const n = Math.min(Math.max(0, Math.floor(count)), this.size());
@@ -103,7 +103,7 @@ export class BufferManager<T> implements IBuffer<T> {
103103
const result = new Array<T>(n);
104104
for (let i = 0; i < n; i++) {
105105
// safe because n <= size()
106-
result[i] = this.buffer.pop(Direction.FRONT) as T;
106+
result[i] = this.buffer.pop(Direction.HEAD) as T;
107107
}
108108
return result;
109109
}
@@ -118,15 +118,15 @@ export class BufferManager<T> implements IBuffer<T> {
118118
popTail(count: number): T[];
119119
popTail(count?: number): T | undefined | T[] {
120120
if (count === undefined) {
121-
return this.buffer.pop(Direction.BACK);
121+
return this.buffer.pop(Direction.TAIL);
122122
}
123123

124124
const n = Math.min(Math.max(0, Math.floor(count)), this.size());
125125
if (n === 0) return [];
126126

127127
const result = new Array<T>(n);
128128
for (let i = 0; i < n; i++) {
129-
result[i] = this.buffer.pop(Direction.BACK) as T;
129+
result[i] = this.buffer.pop(Direction.TAIL) as T;
130130
}
131131
return result;
132132
}
@@ -145,8 +145,8 @@ export class BufferManager<T> implements IBuffer<T> {
145145
getHead(count: number): T[];
146146
getHead(count?: number): T | undefined | T[] {
147147
return count === undefined
148-
? this.buffer.get(Direction.FRONT)
149-
: this.buffer.get(Direction.FRONT, count);
148+
? this.buffer.get(Direction.HEAD)
149+
: this.buffer.get(Direction.HEAD, count);
150150
}
151151

152152
/**
@@ -159,8 +159,8 @@ export class BufferManager<T> implements IBuffer<T> {
159159
getTail(count: number): T[];
160160
getTail(count?: number): T | undefined | T[] {
161161
return count === undefined
162-
? this.buffer.get(Direction.BACK)
163-
: this.buffer.get(Direction.BACK, count);
162+
? this.buffer.get(Direction.TAIL)
163+
: this.buffer.get(Direction.TAIL, count);
164164
}
165165

166166
/**

0 commit comments

Comments
 (0)