Skip to content

Commit d8884f8

Browse files
committed
feat: add TestKeyPrefixContainerCodec and fix fromCodecBuffer
1 parent 28288d8 commit d8884f8

File tree

2 files changed

+129
-50
lines changed

2 files changed

+129
-50
lines changed

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/KeyPrefixContainerCodec.java

Lines changed: 46 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -102,62 +102,58 @@ public KeyPrefixContainer fromCodecBuffer(@Nonnull CodecBuffer buffer) throws Co
102102
final ByteBuffer byteBuffer = buffer.asReadOnlyByteBuffer();
103103
final int totalLength = byteBuffer.remaining();
104104
final int startPosition = byteBuffer.position();
105-
106105
final byte[] delimiterBytes = KEY_DELIMITER.getBytes(UTF_8);
107-
108-
int firstDelimiterIndex = findDelimiterInBuffer(byteBuffer, delimiterBytes);
109-
110-
byteBuffer.position(startPosition);
111-
112-
// If no delimiter found, entire buffer is key prefix
113-
if (firstDelimiterIndex == -1) {
114-
String keyPrefix = decodeStringFromBuffer(byteBuffer, totalLength);
115-
return KeyPrefixContainer.get(keyPrefix, -1, -1);
116-
}
117-
118-
// Decode key prefix without copying
119-
String keyPrefix = decodeStringFromBuffer(byteBuffer, firstDelimiterIndex);
120-
121-
// Skip delimiter
122-
byteBuffer.position(byteBuffer.position() + delimiterBytes.length);
123-
124-
long version = -1;
125-
long containerId = -1;
126-
127-
if (byteBuffer.remaining() >= Long.BYTES) {
128-
version = byteBuffer.getLong();
129-
130-
if (byteBuffer.remaining() >= delimiterBytes.length + Long.BYTES) {
131-
// Skip delimiter
132-
byteBuffer.position(byteBuffer.position() + delimiterBytes.length);
133-
containerId = byteBuffer.getLong();
134-
}
135-
}
136-
137-
return KeyPrefixContainer.get(keyPrefix, version, containerId);
138-
}
139-
140-
/**
141-
* Find delimiter in ByteBuffer without copying data.
142-
* Returns relative position of delimiter, or -1 if not found.
143-
*/
144-
private int findDelimiterInBuffer(ByteBuffer buffer, byte[] delimiter) {
145-
final int startPos = buffer.position();
146-
final int limit = buffer.limit();
147-
148-
for (int i = startPos; i <= limit - delimiter.length; i++) {
149-
boolean found = true;
150-
for (int j = 0; j < delimiter.length; j++) {
151-
if (buffer.get(i + j) != delimiter[j]) {
152-
found = false;
106+
final int delimiterLength = delimiterBytes.length;
107+
108+
// Check if we have at least one delimiter + long value
109+
if (totalLength >= delimiterLength + Long.BYTES) {
110+
// Check for delimiter before the last 8 bytes - could be containerId or version
111+
boolean hasLastDelimiter = true;
112+
int lastDelimiterStart = startPosition + totalLength - Long.BYTES - delimiterLength;
113+
byteBuffer.position(lastDelimiterStart);
114+
for (byte delimiterByte : delimiterBytes) {
115+
if (byteBuffer.get() != delimiterByte) {
116+
hasLastDelimiter = false;
153117
break;
154118
}
155119
}
156-
if (found) {
157-
return i - startPos;
120+
121+
if (hasLastDelimiter) {
122+
// Extract the last value
123+
long lastValue = byteBuffer.getLong();
124+
int remainingLength = lastDelimiterStart - startPosition;
125+
126+
// Check if there's another delimiter+long before this one
127+
if (remainingLength >= delimiterLength + Long.BYTES) {
128+
boolean hasSecondLastDelimiter = true;
129+
int secondLastDelimiterStart = startPosition + remainingLength - Long.BYTES - delimiterLength;
130+
byteBuffer.position(secondLastDelimiterStart);
131+
for (byte delimiterByte : delimiterBytes) {
132+
if (byteBuffer.get() != delimiterByte) {
133+
hasSecondLastDelimiter = false;
134+
break;
135+
}
136+
}
137+
138+
if (hasSecondLastDelimiter) {
139+
long version = byteBuffer.getLong();
140+
141+
byteBuffer.position(startPosition);
142+
String keyPrefix = decodeStringFromBuffer(byteBuffer, secondLastDelimiterStart - startPosition);
143+
return KeyPrefixContainer.get(keyPrefix, version, lastValue);
144+
}
145+
}
146+
147+
// Only one delimiter+value pair - it's a version, not containerId
148+
byteBuffer.position(startPosition);
149+
String keyPrefix = decodeStringFromBuffer(byteBuffer, remainingLength);
150+
return KeyPrefixContainer.get(keyPrefix, lastValue, -1);
158151
}
159152
}
160-
return -1;
153+
154+
byteBuffer.position(startPosition);
155+
String keyPrefix = decodeStringFromBuffer(byteBuffer, totalLength);
156+
return KeyPrefixContainer.get(keyPrefix, -1, -1);
161157
}
162158

163159
/**
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.recon.spi.impl;
19+
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
23+
import org.apache.hadoop.hdds.utils.db.Codec;
24+
import org.apache.hadoop.hdds.utils.db.CodecBuffer;
25+
import org.apache.hadoop.ozone.recon.api.types.KeyPrefixContainer;
26+
import org.junit.jupiter.api.Test;
27+
28+
/**
29+
* Class to test {@link KeyPrefixContainerCodec}.
30+
*/
31+
public class TestKeyPrefixContainerCodec {
32+
33+
private final Codec<KeyPrefixContainer> codec = KeyPrefixContainerCodec.get();
34+
35+
@Test
36+
public void testKeyPrefixOnly() throws Exception {
37+
KeyPrefixContainer original = KeyPrefixContainer.get("testKey");
38+
testCodecBuffer(original);
39+
}
40+
41+
@Test
42+
public void testKeyPrefixAndVersion() throws Exception {
43+
KeyPrefixContainer original = KeyPrefixContainer.get("testKey", 123L);
44+
testCodecBuffer(original);
45+
}
46+
47+
@Test
48+
public void testKeyPrefixVersionAndContainer() throws Exception {
49+
KeyPrefixContainer original = KeyPrefixContainer.get("testKey", 123L, 456L);
50+
testCodecBuffer(original);
51+
}
52+
53+
@Test
54+
public void testEmptyKeyPrefix() throws Exception {
55+
KeyPrefixContainer original = KeyPrefixContainer.get("");
56+
testCodecBuffer(original);
57+
}
58+
59+
@Test
60+
public void testKeyPrefixWithDelimiter() throws Exception {
61+
KeyPrefixContainer original = KeyPrefixContainer.get("test_key_with_underscores", 789L, 101112L);
62+
testCodecBuffer(original);
63+
}
64+
65+
@Test
66+
public void testCodecBufferSupport() {
67+
assertTrue(codec.supportCodecBuffer());
68+
}
69+
70+
@Test
71+
public void testTypeClass() {
72+
assertEquals(KeyPrefixContainer.class, codec.getTypeClass());
73+
}
74+
75+
private void testCodecBuffer(KeyPrefixContainer original) throws Exception {
76+
77+
final CodecBuffer codecBuffer = codec.toCodecBuffer(
78+
original, CodecBuffer.Allocator.getHeap());
79+
final KeyPrefixContainer fromBuffer = codec.fromCodecBuffer(codecBuffer);
80+
codecBuffer.release();
81+
assertEquals(original, fromBuffer);
82+
}
83+
}

0 commit comments

Comments
 (0)