Skip to content

Commit 0edb265

Browse files
committed
Add unsafe operations on the RawCanChannel and cleanup the buffer ordering
This is primarily to better support downstream libraries. See #22
1 parent 3d5f01a commit 0edb265

File tree

12 files changed

+114
-35
lines changed

12 files changed

+114
-35
lines changed

src/main/java/tel/schich/javacan/BcmCanChannel.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import java.nio.ByteBuffer;
2828
import java.nio.ByteOrder;
2929
import java.nio.channels.NotYetBoundException;
30-
import java.nio.channels.spi.SelectorProvider;
3130

3231
import tel.schich.javacan.linux.LinuxNetworkDevice;
3332

@@ -100,7 +99,7 @@ public BcmCanChannel connect(NetworkDevice device) throws IOException {
10099
* @throws IOException if the socket is not readable
101100
*/
102101
public BcmMessage read() throws IOException {
103-
ByteBuffer frameBuf = ByteBuffer.allocateDirect(MTU);
102+
ByteBuffer frameBuf = JavaCAN.allocateOrdered(MTU);
104103
return read(frameBuf);
105104
}
106105

src/main/java/tel/schich/javacan/BcmMessage.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
package tel.schich.javacan;
2424

2525
import java.nio.ByteBuffer;
26-
import java.nio.ByteOrder;
2726
import java.time.Duration;
2827
import java.util.ArrayList;
2928
import java.util.Collections;
@@ -152,10 +151,9 @@ public BcmMessage(BcmOpcode opcode, Set<BcmFlag> flags, int count, Duration inte
152151
int frameLength = frameLength(flags);
153152
base = 0;
154153
size = HEADER_LENGTH + frames.size() * frameLength;
155-
buffer = ByteBuffer.allocateDirect(size);
154+
buffer = JavaCAN.allocateOrdered(size);
156155

157-
buffer.order(ByteOrder.nativeOrder())
158-
.putInt(OFFSET_OPCODE, opcode.nativeOpcode)
156+
buffer.putInt(OFFSET_OPCODE, opcode.nativeOpcode)
159157
.putInt(OFFSET_FLAGS, BcmFlag.toNative(flags))
160158
.putInt(OFFSET_COUNT, count)
161159
.putInt(OFFSET_CAN_ID, canId)
@@ -310,7 +308,7 @@ public ByteBuffer getBuffer() {
310308
}
311309

312310
private ByteBuffer createFrameBuffer(int frameIndex, int frameLength) {
313-
ByteBuffer frameBuffer = buffer.duplicate();
311+
ByteBuffer frameBuffer = buffer.duplicate().order(buffer.order());
314312
frameBuffer.position(base + OFFSET_FRAMES + frameIndex * frameLength)
315313
.limit(frameBuffer.position() + frameLength);
316314
return frameBuffer;

src/main/java/tel/schich/javacan/CanFrame.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -386,15 +386,14 @@ public static CanFrame createRaw(int id, byte flags, byte[] data, int offset, in
386386
} else {
387387
bufSize = RawCanChannel.FD_MTU;
388388
}
389-
ByteBuffer buf = ByteBuffer.allocateDirect(bufSize);
390-
buf.order(ByteOrder.nativeOrder())
391-
.putInt(id)
392-
.put((byte) length)
393-
.put(flags)
394-
.putShort((short) 0) // skip 2 bytes
395-
.put(data, offset, length)
396-
.clear();
397-
return CanFrame.create(buf);
389+
ByteBuffer buffer = JavaCAN.allocateOrdered(bufSize);
390+
buffer.putInt(id)
391+
.put((byte) length)
392+
.put(flags)
393+
.putShort((short) 0) // skip 2 bytes
394+
.put(data, offset, length)
395+
.clear();
396+
return CanFrame.create(buffer);
398397
}
399398

400399
/**
@@ -426,6 +425,9 @@ public static CanFrame create(ByteBuffer buffer) {
426425
* @return the newly created frame
427426
*/
428427
public static CanFrame createUnsafe(ByteBuffer buffer) {
428+
if (buffer.order() != ByteOrder.nativeOrder()) {
429+
throw new IllegalArgumentException("byte order (" + buffer.order() + ") of the given buffer must be the native order (" + ByteOrder.nativeOrder() + ")!");
430+
}
429431
int length = buffer.remaining();
430432
// does the buffer slice size match the non-FD or FD MTU?
431433
if (length != RawCanChannel.MTU && length != RawCanChannel.FD_MTU) {

src/main/java/tel/schich/javacan/CanSocketOptions.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.nio.ByteOrder;
2929
import java.time.Duration;
3030

31-
import tel.schich.javacan.linux.LinuxNativeOperationException;
3231
import tel.schich.javacan.linux.LinuxSocketOptionHandler;
3332
import tel.schich.javacan.option.CanSocketOption;
3433

@@ -127,8 +126,7 @@ public Integer get(int sock) throws IOException {
127126
public static final SocketOption<CanFilter[]> FILTER = new CanSocketOption<>("FILTER", CanFilter[].class, new LinuxSocketOptionHandler<CanFilter[]>() {
128127
@Override
129128
public void set(int sock, CanFilter[] val) throws IOException {
130-
ByteBuffer filterData = ByteBuffer.allocateDirect(val.length * CanFilter.BYTES);
131-
filterData.order(ByteOrder.nativeOrder());
129+
ByteBuffer filterData = JavaCAN.allocateOrdered(val.length * CanFilter.BYTES);
132130
for (CanFilter f : val) {
133131
filterData.putInt(f.getId());
134132
filterData.putInt(f.getMask());

src/main/java/tel/schich/javacan/IsotpCanChannel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,6 @@ public IsotpCanChannel(int sock) {
103103
* @return the newly allocated buffer
104104
*/
105105
public static ByteBuffer allocateSufficientMemory() {
106-
return ByteBuffer.allocateDirect(MAX_MESSAGE_LENGTH + 1);
106+
return JavaCAN.allocateUnordered(MAX_MESSAGE_LENGTH + 1);
107107
}
108108
}

src/main/java/tel/schich/javacan/JavaCAN.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
import java.io.IOException;
2626
import java.io.InputStream;
27+
import java.nio.ByteBuffer;
28+
import java.nio.ByteOrder;
2729
import java.nio.file.Files;
2830
import java.nio.file.Path;
2931

@@ -82,4 +84,24 @@ public synchronized static void initialize() {
8284

8385
initialized = true;
8486
}
87+
88+
/**
89+
* A simple helper to allocate a {@link ByteBuffer} as needed by the underlying native code.
90+
*
91+
* @param capacity the capacity of the buffer.
92+
* @return the buffer in native byte order with the given capacity.
93+
*/
94+
public static ByteBuffer allocateOrdered(int capacity) {
95+
return allocateUnordered(capacity).order(ByteOrder.nativeOrder());
96+
}
97+
98+
/**
99+
* A simple helper to allocate a {@link ByteBuffer} as needed by the underlying native code.
100+
*
101+
* @param capacity the capacity of the buffer.
102+
* @return the buffer in default (unspecified) byte order with the given capacity.
103+
*/
104+
public static ByteBuffer allocateUnordered(int capacity) {
105+
return ByteBuffer.allocateDirect(capacity);
106+
}
85107
}

src/main/java/tel/schich/javacan/RawCanChannel.java

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,64 @@ public RawCanChannel(int sock) {
4747

4848
public abstract RawCanChannel bind(NetworkDevice device) throws IOException;
4949

50+
/**
51+
* Reads a CAM frame from the channel by internally allocating a new direct {@link ByteBuffer}.
52+
*
53+
* @return the CAN frame
54+
* @throws IOException if the IO operations failed or invalid data was read.
55+
*/
5056
public abstract CanFrame read() throws IOException;
57+
58+
/**
59+
* Reads a CAM frame from the channel using the supplied buffer.
60+
*
61+
* @param buffer the buffer to read into.The buffer's {@link ByteOrder} will be set to native and it will be
62+
* flipped after the read has been completed.
63+
* @return the CAN frame
64+
* @throws IOException if the IO operations failed, the supplied buffer was insufficient or invalid data was read.
65+
*/
5166
public abstract CanFrame read(ByteBuffer buffer) throws IOException;
67+
68+
/**
69+
* Reads raw bytes from the channel.
70+
*
71+
* This method does not apply any checks on the data that has been read or on the supplied buffer. This method
72+
* is primarily intended for downstream libraries that implement their own parsing on the data from the socket.
73+
*
74+
* @param buffer the buffer to read into.The buffer's {@link ByteOrder} will be set to native and it will be
75+
* flipped after the read has been completed.
76+
* @return the number of bytes
77+
* @throws IOException if the IO operations failed.
78+
*/
79+
public abstract long readUnsafe(ByteBuffer buffer) throws IOException;
80+
81+
/**
82+
* Writes the given CAN frame.
83+
*
84+
* @param frame the frame to be written.
85+
* @return fluent interface.
86+
* @throws IOException if the IO operations failed.
87+
*/
5288
public abstract RawCanChannel write(CanFrame frame) throws IOException;
5389

90+
/**
91+
* Writes the given buffer in its entirety to the socket.
92+
*
93+
* This method does not apply any checks on the given buffer. This method is primarily intended for downstream libraries
94+
* that create these buffers using other facilities.
95+
*
96+
* @param buffer the buffer to be written.
97+
* @return the bytes written.
98+
* @throws IOException if the IO operations failed.
99+
*/
100+
public abstract long writeUnsafe(ByteBuffer buffer) throws IOException;
101+
102+
/**
103+
* Allocates a buffer that is large enough to hold any supported CAN frame.
104+
*
105+
* @return a new buffer ready to be used.
106+
*/
54107
public static ByteBuffer allocateSufficientMemory() {
55-
ByteBuffer buf = ByteBuffer.allocateDirect(FD_MTU + 1);
56-
buf.order(ByteOrder.nativeOrder());
57-
return buf;
108+
return JavaCAN.allocateOrdered(FD_MTU + 1);
58109
}
59110
}

src/main/java/tel/schich/javacan/RawCanChannelImpl.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
import java.io.IOException;
2626
import java.nio.ByteBuffer;
27-
import java.nio.ByteOrder;
2827
import java.nio.channels.NotYetBoundException;
2928

3029
import tel.schich.javacan.linux.LinuxNetworkDevice;
@@ -66,25 +65,35 @@ public boolean isBound() {
6665
@Override
6766
public CanFrame read() throws IOException {
6867
int length = getOption(CanSocketOptions.FD_FRAMES) ? FD_MTU : MTU;
69-
ByteBuffer frameBuf = ByteBuffer.allocateDirect(length);
68+
ByteBuffer frameBuf = JavaCAN.allocateOrdered(length);
7069
return read(frameBuf);
7170
}
7271

7372
@Override
7473
public CanFrame read(ByteBuffer buffer) throws IOException {
75-
buffer.order(ByteOrder.nativeOrder());
76-
readSocket(buffer);
77-
buffer.flip();
74+
readUnsafe(buffer);
7875
return CanFrame.create(buffer);
7976
}
8077

78+
@Override
79+
public long readUnsafe(ByteBuffer buffer) throws IOException {
80+
long bytesRead = readSocket(buffer);
81+
buffer.flip();
82+
return bytesRead;
83+
}
84+
8185
@Override
8286
public RawCanChannel write(CanFrame frame) throws IOException {
83-
long written = writeSocket(frame.getBuffer());
87+
long written = writeUnsafe(frame.getBuffer());
8488
if (written != frame.getSize()) {
8589
throw new IOException("Frame written incompletely!");
8690
}
8791

8892
return this;
8993
}
94+
95+
@Override
96+
public long writeUnsafe(ByteBuffer buffer) throws IOException {
97+
return writeSocket(buffer);
98+
}
9099
}

src/test/java/tel/schich/javacan/test/BcmCanSocketTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ void testMessageBufferTooSmall() {
8181
data[BcmMessage.OFFSET_NFRAMES] = frameCount;
8282
ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
8383

84-
assertThrows(IllegalArgumentException.class, () -> {
85-
new BcmMessage(buffer);
86-
});
84+
assertThrows(IllegalArgumentException.class, () -> new BcmMessage(buffer));
8785
}
8886

8987
@Test

src/test/java/tel/schich/javacan/test/CanTestHelper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import org.junit.jupiter.api.function.Executable;
2626
import tel.schich.javacan.CanFrame;
27+
import tel.schich.javacan.JavaCAN;
2728
import tel.schich.javacan.NetworkDevice;
2829

2930
import java.io.IOException;
@@ -47,7 +48,7 @@ public static void sendFrameViaUtils(NetworkDevice device, CanFrame frame) throw
4748
if (frame.isRemoteTransmissionRequest()) {
4849
data.append('R');
4950
}
50-
ByteBuffer buf = ByteBuffer.allocateDirect(CanFrame.MAX_FD_DATA_LENGTH);
51+
ByteBuffer buf = JavaCAN.allocateOrdered(CanFrame.MAX_FD_DATA_LENGTH);
5152
frame.getData(buf);
5253
buf.flip();
5354
while (buf.hasRemaining()) {

0 commit comments

Comments
 (0)