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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.hugegraph.backend.id.Id.IdType;
import org.apache.hugegraph.backend.serializer.BytesBuffer;
import org.apache.hugegraph.structure.HugeVertex;
import org.apache.hugegraph.util.Bytes;
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.LongEncoding;
import org.apache.hugegraph.util.NumericUtil;
Expand Down Expand Up @@ -128,14 +129,20 @@ public static int compareType(Id id1, Id id2) {
public static class StringId implements Id {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Test Coverage: This performance optimization changes core behavior of StringId. Please ensure comprehensive test coverage including:\n- Thread safety tests with concurrent access\n- Edge cases with empty/null inputs\n- Performance regression tests\n- Memory usage validation tests


protected String id;
protected byte[] bytes;

public StringId(String id) {
E.checkArgument(!id.isEmpty(), "The id can't be empty");
E.checkArgument(id != null && !id.isEmpty(),
"The id can't be null or empty");
this.id = id;
this.bytes = null;
}

public StringId(byte[] bytes) {
this.id = StringEncoding.decode(bytes);
E.checkArgument(bytes != null && bytes.length > 0,
"The id bytes can't be null or empty");
this.bytes = bytes;
this.id = null;
}

@Override
Expand All @@ -145,27 +152,35 @@ public IdType type() {

@Override
public Object asObject() {
return this.id;
return this.asString();
}

@Override
public String asString() {
if (this.id == null) {
assert this.bytes != null;
this.id = StringEncoding.decode(this.bytes);
}
return this.id;
}

@Override
public long asLong() {
return Long.parseLong(this.id);
return Long.parseLong(this.asString());
}

@Override
public byte[] asBytes() {
return StringEncoding.encode(this.id);
if (this.bytes == null) {
assert this.id != null;
this.bytes = StringEncoding.encode(this.id);
}
return this.bytes;
Copy link

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning the internal byte array directly allows external modification of the StringId's state. Consider returning a defensive copy with return this.bytes.clone(); to maintain immutability.

Suggested change
return this.bytes;
return this.bytes.clone();

Copilot uses AI. Check for mistakes.
}

@Override
public int length() {
return this.id.length();
return this.asString().length();
}

@Override
Expand All @@ -174,25 +189,37 @@ public int compareTo(Id other) {
if (cmp != 0) {
return cmp;
}
return this.id.compareTo(other.asString());
if (this.id != null) {
return this.id.compareTo(other.asString());
} else {
return Bytes.compare(this.bytes, other.asBytes());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance Optimization: Consider using Arrays.equals() instead of Bytes.equals() for byte array comparison if Bytes.equals() is just a wrapper. Also, cache the result of other.asBytes() to avoid multiple conversions in the comparison.

Comment on lines +193 to +195
Copy link

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This optimization to avoid string conversion during comparison may not be effective since other.asBytes() will still trigger encoding if the other StringId only has a string representation. Consider checking if both objects have bytes available before using byte comparison.

Copilot uses AI. Check for mistakes.
}
}

@Override
public int hashCode() {
return this.id.hashCode();
return this.asString().hashCode();
}

@Override
public boolean equals(Object other) {
if (!(other instanceof StringId)) {
public boolean equals(Object obj) {
if (!(obj instanceof StringId)) {
return false;
}
return this.id.equals(((StringId) other).id);
StringId other = (StringId) obj;
if (this.id != null) {
Copy link

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the compareTo method, this optimization may not be effective since other.asString() and other.asBytes() will trigger conversions if the other StringId doesn't have the required representation. Consider optimizing for cases where both objects have the same representation type.

Copilot uses AI. Check for mistakes.
return this.id.equals(other.asString());
} else if (other.bytes == null) {
return this.asString().equals(other.asString());
} else {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Memory Leak: In the equals method, calling other.asBytes() when this.id != null may trigger unnecessary byte array creation and caching in the other object. Consider checking if the other object has the same representation type first to avoid forced conversions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

assert this.bytes != null;
return Bytes.equals(this.bytes, other.asBytes());
}
}

@Override
public String toString() {
return this.id;
return this.asString();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ public Object zeroCopyReadFromByteBuf() {

@Override
public void serializeSelfToByteBuf(MemoryPool memoryPool) {
byte[] stringBytes = id.getBytes((StandardCharsets.UTF_8));
byte[] stringBytes;
if (this.bytes != null) {
stringBytes = this.bytes;
} else {
stringBytes = this.id.getBytes((StandardCharsets.UTF_8));
}
this.idOffHeap = (ByteBuf) memoryPool.requireMemory(stringBytes.length, memoryPool);
this.idOffHeap.markReaderIndex();
this.idOffHeap.writeBytes(stringBytes);
Expand All @@ -70,6 +75,7 @@ public void serializeSelfToByteBuf(MemoryPool memoryPool) {
@Override
public void releaseOriginalVarsOnHeap() {
this.id = null;
this.bytes = null;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in serializeSelfToByteBuf we can set stringBytes = this.bytes if this.bytes != null

}

@Override
Expand Down
Loading