-
Notifications
You must be signed in to change notification settings - Fork 182
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for reading / writing NIO ByteBuffers (#759)
* Add support for reading / writing NIO ByteBuffers Currently one can transfer data using streams or array, but it would be great to have the opportunity to use NIO buffers. This adds two new method to the File class that accept a NIO ByteBuffer. * Implemented ByteBuffer write using ByteChunkProvider Signed-off-by: Jeroen van Erp <[email protected]> --------- Signed-off-by: Jeroen van Erp <[email protected]> Co-authored-by: Christoph Läubrich <[email protected]> Co-authored-by: Jeroen van Erp <[email protected]>
- Loading branch information
1 parent
350c73e
commit 01d720e
Showing
5 changed files
with
265 additions
and
46 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
src/main/java/com/hierynomus/smbj/io/ByteBufferByteChunkProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Copyright (C)2016 - SMBJ Contributors | ||
* | ||
* 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 com.hierynomus.smbj.io; | ||
|
||
import java.io.IOException; | ||
import java.nio.ByteBuffer; | ||
|
||
public class ByteBufferByteChunkProvider extends CachingByteChunkProvider { | ||
private ByteBuffer buffer; | ||
|
||
public ByteBufferByteChunkProvider(ByteBuffer buffer) { | ||
super(); | ||
this.buffer = buffer; | ||
} | ||
|
||
public ByteBufferByteChunkProvider(ByteBuffer buffer, long fileOffset) { | ||
super(); | ||
this.buffer = buffer; | ||
this.offset = fileOffset; | ||
} | ||
|
||
@Override | ||
int prepareChunk(byte[] chunk, int bytesNeeded) throws IOException { | ||
int bytesToRead = Math.min(chunk.length, Math.min(bytesNeeded, buffer.remaining())); | ||
if (bytesToRead == 0) { | ||
return -1; | ||
} | ||
|
||
buffer.get(chunk, 0, bytesToRead); | ||
return bytesToRead; | ||
} | ||
|
||
@Override | ||
public boolean isAvailable() { | ||
return super.isAvailable() || buffer.hasRemaining(); | ||
} | ||
} |
83 changes: 83 additions & 0 deletions
83
src/main/java/com/hierynomus/smbj/io/CachingByteChunkProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* Copyright (C)2016 - SMBJ Contributors | ||
* | ||
* 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 com.hierynomus.smbj.io; | ||
|
||
import java.io.IOException; | ||
|
||
import com.hierynomus.protocol.commons.buffer.Buffer; | ||
import com.hierynomus.protocol.commons.buffer.Endian; | ||
import com.hierynomus.smbj.common.SMBRuntimeException; | ||
|
||
abstract class CachingByteChunkProvider extends ByteChunkProvider { | ||
private BufferByteChunkProvider cachingProvider; | ||
private Buffer<Buffer.PlainBuffer> buffer; | ||
|
||
CachingByteChunkProvider() { | ||
this.buffer = new Buffer.PlainBuffer(Endian.BE); | ||
this.cachingProvider = new BufferByteChunkProvider(buffer); | ||
} | ||
|
||
@Override | ||
public void prepareWrite(int maxBytesToPrepare) { | ||
if (buffer == null) { | ||
return; | ||
} | ||
|
||
byte[] chunk = new byte[1024]; | ||
|
||
// Before each prepareWrite, compact the buffer to minimize size growth | ||
buffer.compact(); | ||
|
||
int bytesNeeded = maxBytesToPrepare - buffer.available(); | ||
int read; | ||
try { | ||
while (bytesNeeded > 0) { | ||
read = prepareChunk(chunk, bytesNeeded); | ||
if (read == -1) { | ||
break; | ||
} | ||
|
||
// Write the data to the buffer | ||
buffer.putRawBytes(chunk, 0, read); | ||
bytesNeeded -= read; | ||
} | ||
} catch (IOException e) { | ||
throw new SMBRuntimeException(e); | ||
} | ||
} | ||
|
||
abstract int prepareChunk(byte[] chunk, int bytesNeeded) throws IOException; | ||
|
||
@Override | ||
protected int getChunk(byte[] chunk) throws IOException { | ||
return cachingProvider.getChunk(chunk); | ||
} | ||
|
||
@Override | ||
public int bytesLeft() { | ||
return cachingProvider.bytesLeft(); | ||
} | ||
|
||
@Override | ||
public boolean isAvailable() { | ||
return cachingProvider.isAvailable(); | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
cachingProvider.close(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
src/test/groovy/com/hierynomus/smbj/io/ByteBufferByteChunkProviderSpec.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright (C)2016 - SMBJ Contributors | ||
* | ||
* 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 com.hierynomus.smbj.io | ||
|
||
import spock.lang.Specification | ||
import java.nio.ByteBuffer | ||
|
||
class ByteBufferByteChunkProviderSpec extends Specification { | ||
|
||
def "should write 1 chunk to outputStream"() { | ||
given: | ||
def buffer = getBufferWithRandomData(ByteChunkProvider.CHUNK_SIZE) | ||
def checkBuff = buffer.duplicate() | ||
def provider = new ByteBufferByteChunkProvider(buffer) | ||
def baos = new ByteArrayOutputStream() | ||
|
||
when: | ||
provider.prepareWrite(ByteChunkProvider.CHUNK_SIZE) | ||
provider.writeChunk(baos) | ||
|
||
then: | ||
baos.toByteArray() == checkBuff.array() | ||
provider.offset == ByteChunkProvider.CHUNK_SIZE | ||
!provider.isAvailable() | ||
} | ||
|
||
def "should write part of chunk to outputStream"() { | ||
given: | ||
def buffer = getBufferWithRandomData(1024) | ||
def checkBuff = buffer.duplicate() | ||
def provider = new ByteBufferByteChunkProvider(buffer) | ||
def baos = new ByteArrayOutputStream() | ||
|
||
when: | ||
provider.prepareWrite(ByteChunkProvider.CHUNK_SIZE) | ||
provider.writeChunk(baos) | ||
|
||
then: | ||
baos.toByteArray() == checkBuff.array() | ||
provider.offset == 1024 | ||
!provider.isAvailable() | ||
|
||
} | ||
|
||
def "should have available after writing first chunk"() { | ||
given: | ||
def buffer = getBufferWithRandomData(ByteChunkProvider.CHUNK_SIZE + 1) | ||
def provider = new ByteBufferByteChunkProvider(buffer) | ||
def baos = new ByteArrayOutputStream() | ||
|
||
when: | ||
provider.prepareWrite(ByteChunkProvider.CHUNK_SIZE) | ||
provider.writeChunk(baos) | ||
|
||
then: | ||
provider.offset == ByteChunkProvider.CHUNK_SIZE | ||
provider.isAvailable() | ||
|
||
} | ||
|
||
private def getBufferWithRandomData(int size) { | ||
def bytes = new byte[size] | ||
new Random().nextBytes(bytes) | ||
def buffer = ByteBuffer.wrap(bytes) | ||
return buffer | ||
} | ||
} |