Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ Common pitfalls and fixes
Linting & style
- 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.

Collections optimization pattern
- 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.
- Example: Check `isEmpty()` before allocating arrays in `keys()`, `values()`, or `iterator()` methods; return `IntArray.empty()`, `Array.empty(type)`, or unmodified containers on empty state.
- This applies across all collection types: `IntToRef`, `LongToRef`, `RefToRef` dictionaries, and array implementations.

Testing conventions
- Use AssertJ for assertions (import `org.assertj.core.api.Assertions`), not JUnit's `Assertions`.
- Test class naming: `<ClassName>Test.java` in the same package under `src/test/java`.
Expand All @@ -68,7 +73,7 @@ Testing conventions

Javadoc conventions
- All public classes, interfaces, and methods should have javadoc.
- Javadoc must include `@since` tag with version (e.g., `@since 10.0.0`).
- 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.
- Use short, active-voice descriptions (e.g., "Returns an array" not "Return an array").
- Do not use trailing periods in `@param` and `@return` descriptions (e.g., `@param value the value` not `@param value the value.`).
- Include `@param` and `@return` tags for non-trivial methods.
Expand Down
163 changes: 163 additions & 0 deletions .github/skills/generate-branch-summary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Skill: Generate Branch Summary

## Purpose
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.

## When to Use
- After completing implementation work on a feature branch
- Before opening a pull request to develop
- When updating branch documentation
- To communicate changes clearly to reviewers

## Process

### 1. Determine Current Branch Context
```bash
git rev-parse --abbrev-ref HEAD # Get current branch name
git log --oneline -5 # Check recent commits
```

### 2. Analyze Changes Relative to Develop
```bash
git diff --stat develop...HEAD # Summary of changed files
git log --oneline develop..HEAD # Commits in this branch
git --no-pager diff develop...HEAD # Full diff (use --no-pager to avoid pager issues)
```

### 3. Categorize Changes
Group modifications by their nature:
- **New APIs/Features** — new interfaces, classes, methods
- **Performance Optimizations** — efficiency improvements, early-exit checks, reduced allocations
- **Bug Fixes** — corrections to existing behavior
- **Refactoring** — internal improvements without behavior change
- **Documentation** — javadoc, comments, instruction files, style guides
- **Version Bumps** — changes to version numbers in build.gradle or other config

### 4. Structure the Summary
Create a section in `summary.md` with this format:

```markdown
# Branch: <branch-name>

**Status:** <Ready for review / In progress / Draft>
**Commits:** <number> (e.g., "1 (abc1234 - 'commit message')" or "3 commits")
**Version:** <old-version> → <new-version> (if applicable)

## Changes Summary

### <Category 1> (e.g., "New Array API")
- Description of what was added/changed
- Key implementation details
- Important behaviors or constraints

### <Category 2> (e.g., "Collection Optimization Pattern")
- Explanation of the optimization
- Which files/classes affected
- Methods modified (count if many)

### <Category 3> (e.g., "Documentation")
- What documentation was added
- Why it matters (e.g., for future contributors)

## Testing & Validation
- `./gradlew :module-name:build` ✅
- `./gradlew clean build` (recommended before merge)
```

### 5. Key Guidelines for Content

#### Be Specific
- ✅ "Added `tryTrimTo(int)` method to safely resize internal storage"
- ❌ "Added new method"

#### Quantify When Helpful
- ✅ "Applied isEmpty() checks to 21 methods across 3 dictionary types"
- ❌ "Applied optimization across dictionaries"

#### Explain Impact
- ✅ "Avoids unnecessary allocations and iterations when collections are empty"
- ❌ "Performance improvement"

#### Include Implementation Details
- ✅ "Returns unchanged if requested size exceeds capacity or is less than current size"
- ❌ "Has validation logic"

#### Use Clear Formatting
- Use markdown headers (`#`, `##`, `###`) for hierarchy
- Use bullet points for lists
- Use bold (`**`) for emphasis on key terms
- Use inline code (`` ` ` ``) for class/method names

### 6. Output Location
Always write to `summary.md` in the repository root, replacing or updating the previous summary section.

### 7. Keep Previous Content
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.

## Example Output

```markdown
# Branch: update-api-part-2

**Status:** Ready for review
**Commits:** 1 (1367d9b - "small improvements")
**Version:** 10.0.alpha13 → 10.0.alpha14

## Changes Summary

### 1. New Array API: `tryTrimTo(int)`
- Added `UnsafeMutableArray.tryTrimTo(int internalStorageSize)` method for safe internal storage resizing
- Implemented in `AbstractMutableArray` with validation:
- Returns unchanged if requested size exceeds current capacity
- Returns unchanged if requested size is less than current element count
- Performs trimming for valid requests
- Includes complete javadoc with `@since 10.0.alpha14` tag

### 2. Collection Optimization Pattern
Applied early-exit `isEmpty()` checks across dictionary implementations:
- **AbstractHashBasedIntToRefDictionary:** 7 methods optimized
- **AbstractHashBasedLongToRefDictionary:** 7 methods optimized
- **AbstractHashBasedRefToRefDictionary:** 7 methods optimized

Optimized methods: `iterator()`, `keys()` (all overloads), `values()` (all overloads)
**Benefit:** Avoids allocations and iterations when collections are empty

### 3. Documentation
- Added comprehensive javadoc to `tryTrimTo()` method
- Updated `.github/copilot-instructions.md` with "Collections optimization pattern" section

## Testing & Validation
- Module build: `./gradlew :rlib-collections:build` ✅
- Full build verification: `./gradlew clean build` (recommended before merge)
```

## Common Patterns in RLib

### API Additions
Always mention:
- Method signature and purpose
- Return type and parameter descriptions
- Javadoc status (whether included, version tag)
- Key behaviors or constraints

### Optimizations
Always mention:
- What was optimized (method names, counts)
- Why (avoid allocations, reduce iterations, etc.)
- Affected files/classes
- Observable impact

### Documentation Changes
Mention:
- What was added or updated
- Why it matters (helps future contributors, clarifies usage, standardizes conventions)
- Files affected

## Tips

1. **Use `git diff --stat`** to get a quick overview of which files changed and how many lines
2. **Use `git log develop..HEAD`** to list all commits in the branch for reference
3. **Extract key insights from the actual diff** — skim the output to understand the nature of changes
4. **Be concise** — summaries should be readable in 2-3 minutes
5. **Link to code** — mention file paths and method names so reviewers can navigate easily
6. **Quantify changes** — "7 methods optimized" is more informative than "several optimizations"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ repositories {
}

ext {
rlibVersion = "10.0.alpha13"
rlibVersion = "10.0.alpha14"
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
rootProject.version = "10.0.alpha13"
rootProject.version = "10.0.alpha14"
group = 'javasabr.rlib'

allprojects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,16 @@ public interface UnsafeMutableArray<E> extends UnsafeArray<E>, MutableArray<E> {
* @since 10.0.0
*/
UnsafeMutableArray<E> trimToSize();

/**
* Attempts to trim the internal storage to the specified size.
*
Comment thread
JavaSaBr marked this conversation as resolved.
* If the requested size is greater than the current capacity or less than the
* current size, the array is not modified.
*
* @param internalStorageSize the desired internal storage size
* @return this for method chaining
* @since 10.0.0
*/
UnsafeMutableArray<E> tryTrimTo(int internalStorageSize);
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,26 @@ public UnsafeMutableArray<E> prepareForSize(int expectedSize) {
public UnsafeMutableArray<E> trimToSize() {
@Nullable E[] wrapped = wrapped();
int size = size();

if (size == wrapped.length) {
return this;
}
wrapped(Arrays.copyOfRange(wrapped, 0, size));
return this;
}

@Override
public UnsafeMutableArray<E> tryTrimTo(int internalStorageSize) {
@Nullable E[] wrapped = wrapped();
int size = size();
if (internalStorageSize >= wrapped.length) {
return this;
} else if (internalStorageSize < size) {
return this;
Comment thread
JavaSaBr marked this conversation as resolved.
}
wrapped(Arrays.copyOf(wrapped, internalStorageSize));
return this;
}

@Override
public UnsafeMutableArray<E> asUnsafe() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ public Optional<V> getOptional(int key) {
public Iterator<V> iterator() {
if (isEmpty()) {
return Collections.emptyIterator();
} else {
return new LinkedRefEntryIterator<>(entries());
}
return new LinkedRefEntryIterator<>(entries());
}

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

@Override
public IntArray keys() {
return IntArray.copyOf(keys(ArrayFactory.mutableIntArray()));
if (isEmpty()) {
return IntArray.empty();
} else {
return IntArray.copyOf(keys(ArrayFactory.mutableIntArray()));
}
}

@Override
public MutableIntArray keys(MutableIntArray container) {

if (isEmpty()) {
return container;
}
UnsafeMutableIntArray unsafe = container.asUnsafe();
unsafe.prepareForSize(container.size() + size());

for (E entry : entries()) {
while (entry != null) {
unsafe.unsafeAdd(entry.key());
entry = entry.next();
}
}

return container;
}

@Override
public Array<Integer> keys(Class<Integer> type) {
return keys(MutableArray.ofType(Integer.class));
if (isEmpty()) {
return Array.empty(type);
} else {
return keys(MutableArray.ofType(type));
}
}

@Override
public MutableArray<Integer> keys(MutableArray<Integer> container) {

if (isEmpty()) {
return container;
}
UnsafeMutableArray<Integer> unsafe = container.asUnsafe();
unsafe.prepareForSize(container.size() + size());

for (E entry : entries()) {
while (entry != null) {
unsafe.unsafeAdd(entry.key());
entry = entry.next();
}
}

return container;
}

@Override
public <C extends Collection<Integer>> C keys(C container) {
if (isEmpty()) {
return container;
}
for (E entry : entries()) {
while (entry != null) {
container.add(entry.key());
Expand All @@ -171,11 +183,18 @@ public <C extends Collection<Integer>> C keys(C container) {

@Override
public Array<V> values(Class<V> type) {
return Array.copyOf(values(ArrayFactory.mutableArray(type, size())));
if (isEmpty()) {
return Array.empty(type);
} else {
return Array.copyOf(values(ArrayFactory.mutableArray(type, size())));
}
}

@Override
public <C extends Collection<V>> C values(C container) {
if (isEmpty()) {
return container;
}
for (E entry : entries()) {
while (entry != null) {
V value = entry.value();
Expand All @@ -190,10 +209,11 @@ public <C extends Collection<V>> C values(C container) {

@Override
public MutableArray<V> values(MutableArray<V> container) {

if (isEmpty()) {
return container;
}
UnsafeMutableArray<V> unsafe = container.asUnsafe();
unsafe.prepareForSize(container.size() + size());

for (E entry : entries()) {
while (entry != null) {
V value = entry.value();
Expand All @@ -203,7 +223,6 @@ public MutableArray<V> values(MutableArray<V> container) {
entry = entry.next();
}
}

return container;
}
}
Loading
Loading