Skip to content

Commit 4e6b556

Browse files
authored
Small improvements, part 2 (#65)
1 parent 1866161 commit 4e6b556

9 files changed

Lines changed: 284 additions & 36 deletions

File tree

.github/copilot-instructions.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ Common pitfalls and fixes
5151
Linting & style
5252
- The root does not expose an obvious global formatting tool (no Spotless or root Checkstyle detected). Use the existing code style. Run `./gradlew check` to execute configured verification tasks.
5353

54+
Collections optimization pattern
55+
- In collection implementations (dictionaries, arrays), use early-exit `isEmpty()` checks to avoid unnecessary allocations and iterations. Return empty collections or unmodified containers immediately when the collection is empty.
56+
- Example: Check `isEmpty()` before allocating arrays in `keys()`, `values()`, or `iterator()` methods; return `IntArray.empty()`, `Array.empty(type)`, or unmodified containers on empty state.
57+
- This applies across all collection types: `IntToRef`, `LongToRef`, `RefToRef` dictionaries, and array implementations.
58+
5459
Testing conventions
5560
- Use AssertJ for assertions (import `org.assertj.core.api.Assertions`), not JUnit's `Assertions`.
5661
- Test class naming: `<ClassName>Test.java` in the same package under `src/test/java`.
@@ -68,7 +73,7 @@ Testing conventions
6873

6974
Javadoc conventions
7075
- All public classes, interfaces, and methods should have javadoc.
71-
- Javadoc must include `@since` tag with version (e.g., `@since 10.0.0`).
76+
- Javadoc must include `@since` tag with version (e.g., `@since 10.0.0`). Use the base GA version, not pre-release qualifiers (e.g., use `10.0.0` not `10.0.alpha14`), even if `build.gradle` specifies a pre-release version.
7277
- Use short, active-voice descriptions (e.g., "Returns an array" not "Return an array").
7378
- Do not use trailing periods in `@param` and `@return` descriptions (e.g., `@param value the value` not `@param value the value.`).
7479
- Include `@param` and `@return` tags for non-trivial methods.
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Skill: Generate Branch Summary
2+
3+
## Purpose
4+
Generate a concise, structured summary of all changes in the current branch compared to the develop branch. This summary is written to `summary.md` and serves as the single source of truth for understanding what a branch introduces.
5+
6+
## When to Use
7+
- After completing implementation work on a feature branch
8+
- Before opening a pull request to develop
9+
- When updating branch documentation
10+
- To communicate changes clearly to reviewers
11+
12+
## Process
13+
14+
### 1. Determine Current Branch Context
15+
```bash
16+
git rev-parse --abbrev-ref HEAD # Get current branch name
17+
git log --oneline -5 # Check recent commits
18+
```
19+
20+
### 2. Analyze Changes Relative to Develop
21+
```bash
22+
git diff --stat develop...HEAD # Summary of changed files
23+
git log --oneline develop..HEAD # Commits in this branch
24+
git --no-pager diff develop...HEAD # Full diff (use --no-pager to avoid pager issues)
25+
```
26+
27+
### 3. Categorize Changes
28+
Group modifications by their nature:
29+
- **New APIs/Features** — new interfaces, classes, methods
30+
- **Performance Optimizations** — efficiency improvements, early-exit checks, reduced allocations
31+
- **Bug Fixes** — corrections to existing behavior
32+
- **Refactoring** — internal improvements without behavior change
33+
- **Documentation** — javadoc, comments, instruction files, style guides
34+
- **Version Bumps** — changes to version numbers in build.gradle or other config
35+
36+
### 4. Structure the Summary
37+
Create a section in `summary.md` with this format:
38+
39+
```markdown
40+
# Branch: <branch-name>
41+
42+
**Status:** <Ready for review / In progress / Draft>
43+
**Commits:** <number> (e.g., "1 (abc1234 - 'commit message')" or "3 commits")
44+
**Version:** <old-version> → <new-version> (if applicable)
45+
46+
## Changes Summary
47+
48+
### <Category 1> (e.g., "New Array API")
49+
- Description of what was added/changed
50+
- Key implementation details
51+
- Important behaviors or constraints
52+
53+
### <Category 2> (e.g., "Collection Optimization Pattern")
54+
- Explanation of the optimization
55+
- Which files/classes affected
56+
- Methods modified (count if many)
57+
58+
### <Category 3> (e.g., "Documentation")
59+
- What documentation was added
60+
- Why it matters (e.g., for future contributors)
61+
62+
## Testing & Validation
63+
- `./gradlew :module-name:build`
64+
- `./gradlew clean build` (recommended before merge)
65+
```
66+
67+
### 5. Key Guidelines for Content
68+
69+
#### Be Specific
70+
- ✅ "Added `tryTrimTo(int)` method to safely resize internal storage"
71+
- ❌ "Added new method"
72+
73+
#### Quantify When Helpful
74+
- ✅ "Applied isEmpty() checks to 21 methods across 3 dictionary types"
75+
- ❌ "Applied optimization across dictionaries"
76+
77+
#### Explain Impact
78+
- ✅ "Avoids unnecessary allocations and iterations when collections are empty"
79+
- ❌ "Performance improvement"
80+
81+
#### Include Implementation Details
82+
- ✅ "Returns unchanged if requested size exceeds capacity or is less than current size"
83+
- ❌ "Has validation logic"
84+
85+
#### Use Clear Formatting
86+
- Use markdown headers (`#`, `##`, `###`) for hierarchy
87+
- Use bullet points for lists
88+
- Use bold (`**`) for emphasis on key terms
89+
- Use inline code (`` ` ` ``) for class/method names
90+
91+
### 6. Output Location
92+
Always write to `summary.md` in the repository root, replacing or updating the previous summary section.
93+
94+
### 7. Keep Previous Content
95+
If `summary.md` already contains unrelated sections (e.g., documentation of other work), preserve them unless explicitly asked to remove them. Only update or replace the branch summary section.
96+
97+
## Example Output
98+
99+
```markdown
100+
# Branch: update-api-part-2
101+
102+
**Status:** Ready for review
103+
**Commits:** 1 (1367d9b - "small improvements")
104+
**Version:** 10.0.alpha13 → 10.0.alpha14
105+
106+
## Changes Summary
107+
108+
### 1. New Array API: `tryTrimTo(int)`
109+
- Added `UnsafeMutableArray.tryTrimTo(int internalStorageSize)` method for safe internal storage resizing
110+
- Implemented in `AbstractMutableArray` with validation:
111+
- Returns unchanged if requested size exceeds current capacity
112+
- Returns unchanged if requested size is less than current element count
113+
- Performs trimming for valid requests
114+
- Includes complete javadoc with `@since 10.0.alpha14` tag
115+
116+
### 2. Collection Optimization Pattern
117+
Applied early-exit `isEmpty()` checks across dictionary implementations:
118+
- **AbstractHashBasedIntToRefDictionary:** 7 methods optimized
119+
- **AbstractHashBasedLongToRefDictionary:** 7 methods optimized
120+
- **AbstractHashBasedRefToRefDictionary:** 7 methods optimized
121+
122+
Optimized methods: `iterator()`, `keys()` (all overloads), `values()` (all overloads)
123+
**Benefit:** Avoids allocations and iterations when collections are empty
124+
125+
### 3. Documentation
126+
- Added comprehensive javadoc to `tryTrimTo()` method
127+
- Updated `.github/copilot-instructions.md` with "Collections optimization pattern" section
128+
129+
## Testing & Validation
130+
- Module build: `./gradlew :rlib-collections:build`
131+
- Full build verification: `./gradlew clean build` (recommended before merge)
132+
```
133+
134+
## Common Patterns in RLib
135+
136+
### API Additions
137+
Always mention:
138+
- Method signature and purpose
139+
- Return type and parameter descriptions
140+
- Javadoc status (whether included, version tag)
141+
- Key behaviors or constraints
142+
143+
### Optimizations
144+
Always mention:
145+
- What was optimized (method names, counts)
146+
- Why (avoid allocations, reduce iterations, etc.)
147+
- Affected files/classes
148+
- Observable impact
149+
150+
### Documentation Changes
151+
Mention:
152+
- What was added or updated
153+
- Why it matters (helps future contributors, clarifies usage, standardizes conventions)
154+
- Files affected
155+
156+
## Tips
157+
158+
1. **Use `git diff --stat`** to get a quick overview of which files changed and how many lines
159+
2. **Use `git log develop..HEAD`** to list all commits in the branch for reference
160+
3. **Extract key insights from the actual diff** — skim the output to understand the nature of changes
161+
4. **Be concise** — summaries should be readable in 2-3 minutes
162+
5. **Link to code** — mention file paths and method names so reviewers can navigate easily
163+
6. **Quantify changes** — "7 methods optimized" is more informative than "several optimizations"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ repositories {
4545
}
4646
4747
ext {
48-
rlibVersion = "10.0.alpha13"
48+
rlibVersion = "10.0.alpha14"
4949
}
5050
5151
dependencies {

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
rootProject.version = "10.0.alpha13"
1+
rootProject.version = "10.0.alpha14"
22
group = 'javasabr.rlib'
33

44
allprojects {

rlib-collections/src/main/java/javasabr/rlib/collections/array/UnsafeMutableArray.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,16 @@ public interface UnsafeMutableArray<E> extends UnsafeArray<E>, MutableArray<E> {
5353
* @since 10.0.0
5454
*/
5555
UnsafeMutableArray<E> trimToSize();
56+
57+
/**
58+
* Attempts to trim the internal storage to the specified size.
59+
*
60+
* If the requested size is greater than the current capacity or less than the
61+
* current size, the array is not modified.
62+
*
63+
* @param internalStorageSize the desired internal storage size
64+
* @return this for method chaining
65+
* @since 10.0.0
66+
*/
67+
UnsafeMutableArray<E> tryTrimTo(int internalStorageSize);
5668
}

rlib-collections/src/main/java/javasabr/rlib/collections/array/impl/AbstractMutableArray.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,26 @@ public UnsafeMutableArray<E> prepareForSize(int expectedSize) {
214214
public UnsafeMutableArray<E> trimToSize() {
215215
@Nullable E[] wrapped = wrapped();
216216
int size = size();
217-
218217
if (size == wrapped.length) {
219218
return this;
220219
}
221220
wrapped(Arrays.copyOfRange(wrapped, 0, size));
222221
return this;
223222
}
224223

224+
@Override
225+
public UnsafeMutableArray<E> tryTrimTo(int internalStorageSize) {
226+
@Nullable E[] wrapped = wrapped();
227+
int size = size();
228+
if (internalStorageSize >= wrapped.length) {
229+
return this;
230+
} else if (internalStorageSize < size) {
231+
return this;
232+
}
233+
wrapped(Arrays.copyOf(wrapped, internalStorageSize));
234+
return this;
235+
}
236+
225237
@Override
226238
public UnsafeMutableArray<E> asUnsafe() {
227239
return this;

rlib-collections/src/main/java/javasabr/rlib/collections/dictionary/impl/AbstractHashBasedIntToRefDictionary.java

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ public Optional<V> getOptional(int key) {
8585
public Iterator<V> iterator() {
8686
if (isEmpty()) {
8787
return Collections.emptyIterator();
88+
} else {
89+
return new LinkedRefEntryIterator<>(entries());
8890
}
89-
return new LinkedRefEntryIterator<>(entries());
9091
}
9192

9293
@Nullable
@@ -118,48 +119,59 @@ public void forEach(IntObjConsumer<V> consumer) {
118119

119120
@Override
120121
public IntArray keys() {
121-
return IntArray.copyOf(keys(ArrayFactory.mutableIntArray()));
122+
if (isEmpty()) {
123+
return IntArray.empty();
124+
} else {
125+
return IntArray.copyOf(keys(ArrayFactory.mutableIntArray()));
126+
}
122127
}
123128

124129
@Override
125130
public MutableIntArray keys(MutableIntArray container) {
126-
131+
if (isEmpty()) {
132+
return container;
133+
}
127134
UnsafeMutableIntArray unsafe = container.asUnsafe();
128135
unsafe.prepareForSize(container.size() + size());
129-
130136
for (E entry : entries()) {
131137
while (entry != null) {
132138
unsafe.unsafeAdd(entry.key());
133139
entry = entry.next();
134140
}
135141
}
136-
137142
return container;
138143
}
139144

140145
@Override
141146
public Array<Integer> keys(Class<Integer> type) {
142-
return keys(MutableArray.ofType(Integer.class));
147+
if (isEmpty()) {
148+
return Array.empty(type);
149+
} else {
150+
return keys(MutableArray.ofType(type));
151+
}
143152
}
144153

145154
@Override
146155
public MutableArray<Integer> keys(MutableArray<Integer> container) {
147-
156+
if (isEmpty()) {
157+
return container;
158+
}
148159
UnsafeMutableArray<Integer> unsafe = container.asUnsafe();
149160
unsafe.prepareForSize(container.size() + size());
150-
151161
for (E entry : entries()) {
152162
while (entry != null) {
153163
unsafe.unsafeAdd(entry.key());
154164
entry = entry.next();
155165
}
156166
}
157-
158167
return container;
159168
}
160169

161170
@Override
162171
public <C extends Collection<Integer>> C keys(C container) {
172+
if (isEmpty()) {
173+
return container;
174+
}
163175
for (E entry : entries()) {
164176
while (entry != null) {
165177
container.add(entry.key());
@@ -171,11 +183,18 @@ public <C extends Collection<Integer>> C keys(C container) {
171183

172184
@Override
173185
public Array<V> values(Class<V> type) {
174-
return Array.copyOf(values(ArrayFactory.mutableArray(type, size())));
186+
if (isEmpty()) {
187+
return Array.empty(type);
188+
} else {
189+
return Array.copyOf(values(ArrayFactory.mutableArray(type, size())));
190+
}
175191
}
176192

177193
@Override
178194
public <C extends Collection<V>> C values(C container) {
195+
if (isEmpty()) {
196+
return container;
197+
}
179198
for (E entry : entries()) {
180199
while (entry != null) {
181200
V value = entry.value();
@@ -190,10 +209,11 @@ public <C extends Collection<V>> C values(C container) {
190209

191210
@Override
192211
public MutableArray<V> values(MutableArray<V> container) {
193-
212+
if (isEmpty()) {
213+
return container;
214+
}
194215
UnsafeMutableArray<V> unsafe = container.asUnsafe();
195216
unsafe.prepareForSize(container.size() + size());
196-
197217
for (E entry : entries()) {
198218
while (entry != null) {
199219
V value = entry.value();
@@ -203,7 +223,6 @@ public MutableArray<V> values(MutableArray<V> container) {
203223
entry = entry.next();
204224
}
205225
}
206-
207226
return container;
208227
}
209228
}

0 commit comments

Comments
 (0)