-
Notifications
You must be signed in to change notification settings - Fork 0
Optimize children lookup O(N)→O(1), add path traversal prevention, expand test coverage #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize children lookup O(N)→O(1), add path traversal prevention, expand test coverage #6
Conversation
- Optimize WebDavFile children lookup from O(N) to O(1) using HashMap - Add path traversal prevention in parseDocumentId - Add comprehensive unit tests for Account, WebDavFile, and WebDavProvider Co-authored-by: MinecraftFuns <[email protected]>
- Use path segment analysis instead of string pattern matching - Allow filenames like 'file..backup' while blocking '../' traversal - Fix error message to say 'non-negative integer' for consistency Co-authored-by: MinecraftFuns <[email protected]>
- Use idiomatic Kotlin .also{} pattern instead of !! operator
- Replace flaky timing-based performance test with correctness test
Co-authored-by: MinecraftFuns <[email protected]>
|
@copilot please evaluate this LinkedHashMap idea and see if you could remove index-based access pattern entirely so we can have true O(1) insertion+deletion+ordered access (we don't need any backward compatibility for the codebase so long as our codebase is self-consistent; since this is an application and not a library); also since this code change involves complex data structures please add more test cases to ensure its correctness under more challenging access patterns; also please create clear documentation in the docs/ to carefully analyze and compare the data structures that could be used and choose the most performant one that can satisfy our current needs (note that you don't need to maintain "backward compatibility"; so long as your data structure satisfies the current access needs it should be fine). Please follow best practices and write idiomatic code and ensure comprehensive documentation & test coverage. === review docs fyi === This is a sophisticated review request. Your wrapper implementation is a pragmatic solution to retroactively add O(1) lookup to a legacy list-based system. Here is the review of the code provided and an evaluation of your alternative algorithm proposal. 1. Code Review:
|
| Operation | Current (ArrayList + Map) | Proposed (DLL + Map) |
|---|---|---|
| Lookup by Path | O(1) | O(1) |
Lookup by Index (get(i)) |
O(1) | O(N) (Must traverse list) |
| Add/Remove (at End) | O(1) | O(1) |
| Add/Remove (Middle) | O(N) (Array shift) | O(1) (Pointer swap)* |
*Note: Removal in a DLL is only O(1) if you already have the reference to the Node. If you have to find the node by index first, it's O(N).
Analysis
- The "O(1) Everything" Myth: A DLL + HashMap gives you O(1) lookup and O(1) removal by key. However, it sacrifices O(1) indexed access. If your legacy code ever does
children.get(i)or iterates using an index loopfor (i in 0 until size), the performance will degrade to O(N) per access (making the loop O(N²)). - Existing Solution: Java/Kotlin's standard
LinkedHashMapis effectively this data structure (Hash Table + Doubly Linked List preserving insertion order). - Recommendation:
- Stick to your
ChildrenListWrapperif external callers rely onListsemantics (random access by index). The cost of O(N) removal is usually acceptable for file directories (which rarely have millions of entries), whereas O(N) indexed access would be a disaster for UI adapters or validaters. - Switch to
LinkedHashMaponly if you can refactorchildrento be aMaporCollectionand drop theListinterface entirely. This avoids the "O(N) indexed access" trap by removing the API that allows it.
- Stick to your
Summary
Your current ChildrenListWrapper is the correct approach for a drop-in replacement that preserves List behavior. Just ensure WebDavFile.path is immutable to prevent cache corruption.[1][2]
- Use LinkedHashMap for children collection with O(1) add/remove/lookup - Make path immutable (val) to ensure HashMap key stability - Add explicit children management API (addChild, removeChild, etc.) - Create comprehensive documentation in docs/DATA_STRUCTURES.md - Update all usages in WebDavProvider, WebDavClient, WebDavCache - Expand test coverage to 80 tests Co-authored-by: MinecraftFuns <[email protected]>
- Add copyWithNewPath() to WebDavFile for safe property copying - Update WebDavProvider.renameDocument to use the new method - Add tests for copyWithNewPath method - Total: 82 unit tests Co-authored-by: MinecraftFuns <[email protected]>
- Add ConcurrentModificationException warning to children() method - Enhance test class KDoc with specific test categories Co-authored-by: MinecraftFuns <[email protected]>
Implemented in commits b28bb10 through 81b9e58. Changes:
Test coverage: 82 unit tests including:
|
|
@copilot please review the following concerns carefully This optimization is generally safe and effectively speeds up lookups, but there is one specific bug/regression risk in The Optimization: Summary
The Risk: "Pending" File OverwritesIn // In createDocument (WebDavProvider.kt)
} else {
val file = WebDavFile(path, false, contentType = mimeType, isPending = true)
file.parent = dir
dir.children.add(file) // Replaced by dir.addChild(file)
// ...
}The Issue: Scenario:
Mitigation: // In openDocumentWrite failure callback
onFail = {
file.parent?.removeChild(file) // Removes the pending file
// Does NOT explicitly restore the old file if one existed!
}If RecommendationThe optimization is safe to merge with one small caveat:
Code Quality Note: Final Verdict: Approve, but test the "failed overwrite" scenario.
This diff represents a high-quality, robust engineering change. The optimization is sound, mathematically justified (O(N) → O(1)), and the accompanying refactoring significantly improves type safety and security. Here is a detailed review of the changes: 1. Optimization Logic (Performance)
2. Correctness & Type Safety
3. Security Improvements
4. Code Quality & Testing
Potential Risks (Minor)
ConclusionMerge this. It is a significant improvement in performance and code quality with no apparent downsides. It transforms a naive list implementation into a production-grade data structure. |
- Add section explaining why ConcurrentModificationException cannot occur - Document the DocumentsProvider threading model (Binder threads, runBlocking) - Explain that original ArrayList had same concurrency characteristics - Document improved duplicate path handling (overwrite vs accumulate) - Explain pending file behavior and edge cases Co-authored-by: MinecraftFuns <[email protected]>
... Addressed in commit 5614cfa. Updated 1. Threading Model and Concurrency Safety
2. Duplicate Path Handling
|
- Use "current codebase (December 2024)" instead of absolute claims - Change "cannot occur" to "should not occur during normal operation" - Add note about using childrenSnapshot() for future code changes - Frame duplicate upload handling as "design decision" not "correctness" Co-authored-by: MinecraftFuns <[email protected]>
Summary
Complete refactoring of
WebDavFile.childrenfromArrayListto pureLinkedHashMapfor true O(1) operations.Changes
Data Structure
ArrayList<WebDavFile>+HashMap<Path, WebDavFile>with complex wrapper class (~120 lines)LinkedHashMap<Path, WebDavFile>with explicit API methods (~50 lines)Performance (all O(1))
addChild(child)removeChild(child)/removeChildByPath(path)findChildByPath(path)containsChild(path)childCountKey Improvements
pathis nowvalto prevent HashMap key corruptionaddChild(),removeChild()instead of generic list operationsDocumentation
docs/DATA_STRUCTURES.md:Test Coverage
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.