Buffers: add write helpers writeLengthPrefixedBytes() and writeLengthPrefixedString()

Includes a test.
This commit is contained in:
Andreas Schildbach 2023-04-03 23:26:23 +02:00
parent fbc5185b5a
commit 06070eeaf2
2 changed files with 85 additions and 0 deletions

View File

@ -18,6 +18,7 @@ package org.bitcoinj.base.internal;
import org.bitcoinj.base.VarInt;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@ -59,6 +60,18 @@ public class Buffers {
return readBytes(buf, length);
}
/**
* First write the length of the byte array as a {@link VarInt}. Then write the array contents.
*
* @param buf buffer to write to
* @param bytes bytes to write
* @return the buffer
* @throws BufferOverflowException if the value doesn't fit the remaining buffer
*/
public static ByteBuffer writeLengthPrefixedBytes(ByteBuffer buf, byte[] bytes) throws BufferOverflowException {
return buf.put(VarInt.of(bytes.length).serialize()).put(bytes);
}
/**
* First read a {@link VarInt} from the buffer and use it to determine the number of bytes to read. Then read
* that many bytes and interpret it as an UTF-8 encoded string to be returned. This construct is frequently used
@ -72,6 +85,20 @@ public class Buffers {
return new String(readLengthPrefixedBytes(buf), StandardCharsets.UTF_8);
}
/**
* Encode a given string using UTF-8. Then write the lnegth of the encoded bytes as a {@link VarInt}. Then write
* the bytes themselves.
*
* @param buf buffer to write to
* @param str string to write
* @return the buffer
* @throws BufferOverflowException if the value doesn't fit the remaining buffer
*/
public static ByteBuffer writeLengthPrefixedString(ByteBuffer buf, String str) throws BufferOverflowException {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
return writeLengthPrefixedBytes(buf, bytes);
}
/**
* Advance buffer position by a given number of bytes.
*

View File

@ -0,0 +1,58 @@
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.bitcoinj.base.VarInt;
import org.bitcoinj.base.internal.Buffers;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Random;
import java.util.stream.Stream;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
@RunWith(JUnitParamsRunner.class)
public class BuffersTest {
@Test
@Parameters(method = "randomBytes")
public void readAndWrite(byte[] bytes) {
ByteBuffer buf = ByteBuffer.allocate(VarInt.sizeOf(bytes.length) + bytes.length);
Buffers.writeLengthPrefixedBytes(buf, bytes);
assertFalse(buf.hasRemaining());
((Buffer) buf).rewind();
byte[] copy = Buffers.readLengthPrefixedBytes(buf);
assertFalse(buf.hasRemaining());
assertArrayEquals(bytes, copy);
}
private Iterator<byte[]> randomBytes() {
Random random = new Random();
return Stream.generate(() -> {
int length = random.nextInt(10);
byte[] bytes = new byte[length];
random.nextBytes(bytes);
return bytes;
}).limit(10).iterator();
}
}