diff --git a/bson/src/main/org/bson/ByteBuf.java b/bson/src/main/org/bson/ByteBuf.java
index e44a97dfc67..ba6608c513f 100644
--- a/bson/src/main/org/bson/ByteBuf.java
+++ b/bson/src/main/org/bson/ByteBuf.java
@@ -168,6 +168,23 @@ public interface ByteBuf  {
      */
     ByteBuf clear();
 
+
+    /**
+     * Returns the index within this buffer of the first occurrence of the given byte
+     *
+     * @param b     The byte to search for
+     * @return The index within this buffer of the first occurrence of the given byte, starting the search at the specified index, or {@code
+     * -1} if the byte does not occur before the limit
+     */
+    default int indexOf(byte b) {
+        for (int i = position(); i < limit(); i++) {
+            if (get(i) == b) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     /**
      * Modifies this buffer's byte order.
      *
diff --git a/bson/src/main/org/bson/ByteBufNIO.java b/bson/src/main/org/bson/ByteBufNIO.java
index 83bfa7d893a..c86c5266a9c 100644
--- a/bson/src/main/org/bson/ByteBufNIO.java
+++ b/bson/src/main/org/bson/ByteBufNIO.java
@@ -64,6 +64,31 @@ public void release() {
         }
     }
 
+    @Override
+    public int indexOf(final byte b) {
+        ByteBuffer buf = this.buf;
+        // readonly buffers won't go into the fast-path
+        if (buf.hasArray()) {
+            byte[] array = buf.array();
+            int offset = buf.arrayOffset() + buf.position();
+            int length = buf.remaining();
+            for (int i = 0; i < length; i++) {
+                if (array[offset + i] == b) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+        int position = buf.position();
+        int limit = buf.limit();
+        for (int i = position; i < limit; i++) {
+            if (buf.get(i) == b) {
+                return i - position;
+            }
+        }
+        return -1;
+    }
+
     @Override
     public int capacity() {
         return buf.capacity();
diff --git a/bson/src/main/org/bson/io/ByteBufferBsonInput.java b/bson/src/main/org/bson/io/ByteBufferBsonInput.java
index a5a0e7a5421..b8159fbc0a4 100644
--- a/bson/src/main/org/bson/io/ByteBufferBsonInput.java
+++ b/bson/src/main/org/bson/io/ByteBufferBsonInput.java
@@ -159,13 +159,12 @@ private String readString(final int size) {
     @Override
     public void skipCString() {
         ensureOpen();
-        boolean checkNext = true;
-        while (checkNext) {
-            if (!buffer.hasRemaining()) {
-                throw new BsonSerializationException("Found a BSON string that is not null-terminated");
-            }
-            checkNext = buffer.get() != 0;
+        int indexOfZero = buffer.indexOf((byte) 0);
+        if (indexOfZero == -1) {
+            buffer.position(buffer.limit());
+            throw new BsonSerializationException("Found a BSON string that is not null-terminated");
         }
+        buffer.position(indexOfZero + 1);
     }
 
     @Override
diff --git a/driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java b/driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java
index fa8cde2e517..652b3343226 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java
@@ -42,7 +42,9 @@ class CompositeByteBuf implements ByteBuf {
 
         int offset = 0;
         for (ByteBuf cur : buffers) {
-            Component component = new Component(cur.asReadOnly().order(ByteOrder.LITTLE_ENDIAN), offset);
+            // since we don't expose any method to modify the buffer nor expose its array we can avoid using read-only ones
+            // to speed up ByteBufNIO::indexOf, is it's the concrete type used
+            Component component = new Component(cur.duplicate().order(ByteOrder.LITTLE_ENDIAN), offset);
             components.add(component);
             offset = component.endOffset;
         }
@@ -63,6 +65,18 @@ public ByteBuf order(final ByteOrder byteOrder) {
         return this;
     }
 
+    @Override
+    public int indexOf(final byte b) {
+        // use this pattern to save creating a new iterator
+        for (int i = 0, size = components.size(); i < size; i++) {
+            int index = components.get(i).indexOf(b);
+            if (index != -1) {
+                return index;
+            }
+        }
+        return -1;
+    }
+
     @Override
     public int capacity() {
         return components.get(components.size() - 1).endOffset;
@@ -340,5 +354,13 @@ private static final class Component {
             this.offset = offset;
             this.endOffset = offset + length;
         }
+
+        public int indexOf(final byte b) {
+            int i = buffer.indexOf(b);
+            if (i != -1) {
+                return i + offset;
+            }
+            return -1;
+        }
     }
 }
diff --git a/driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java b/driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java
index 074e77de04f..b0445a0c0ad 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java
@@ -89,6 +89,16 @@ public ByteBuf put(final byte b) {
         return this;
     }
 
+    @Override
+    public int indexOf(final byte b) {
+        int position = isWriting ? proxied.writerIndex() : proxied.readerIndex();
+        int limit = position + (isWriting ? proxied.writableBytes() : proxied.readableBytes());
+        if (position == limit) {
+            return -1;
+        }
+        return proxied.indexOf(position, limit, b);
+    }
+
     @Override
     public ByteBuf flip() {
         isWriting = !isWriting;
diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufSpecification.groovy
index 0e0755f65bd..770c49290c6 100644
--- a/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufSpecification.groovy
+++ b/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufSpecification.groovy
@@ -24,6 +24,56 @@ import org.bson.ByteBuf
 import spock.lang.Specification
 
 class ByteBufSpecification extends Specification {
+
+
+    def "should find the first occurrence of a byte"() {
+        given:
+        def buffer = provider.getBuffer(1024)
+
+        when:
+        buffer.with {
+            put((byte) 1)
+            put((byte) 2)
+            put((byte) 3)
+            put((byte) 4)
+            put((byte) 5)
+            flip()
+        }
+
+        then:
+        buffer.indexOf((byte) 3) == 2
+
+        cleanup:
+        buffer.release()
+
+        where:
+        provider << [new NettyBufferProvider(), new SimpleBufferProvider()]
+    }
+
+    def "should not find any occurrence of a byte"() {
+            given:
+        def buffer = provider.getBuffer(1024)
+
+        when:
+        buffer.with {
+            put((byte) 1)
+            put((byte) 2)
+            put((byte) 3)
+            put((byte) 4)
+            put((byte) 5)
+            flip()
+        }
+
+        then:
+        buffer.indexOf((byte) 6) == -1
+
+        cleanup:
+        buffer.release()
+
+        where:
+        provider << [new NettyBufferProvider(), new SimpleBufferProvider()]
+    }
+
     def 'should put a byte'() {
         given:
         def buffer = provider.getBuffer(1024)
diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/CompositeByteBufSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/CompositeByteBufSpecification.groovy
index 6c43e2a667e..ca0cd9b3114 100644
--- a/driver-core/src/test/unit/com/mongodb/internal/connection/CompositeByteBufSpecification.groovy
+++ b/driver-core/src/test/unit/com/mongodb/internal/connection/CompositeByteBufSpecification.groovy
@@ -290,6 +290,37 @@ class CompositeByteBufSpecification extends Specification {
         !buf.hasRemaining()
     }
 
+    def "should find the first occurrence of a byte"() {
+        given:
+        def buf = new CompositeByteBuf([
+                        new ByteBufNIO(ByteBuffer.wrap([5, 5, 5, 5] as byte[])),
+                        new ByteBufNIO(ByteBuffer.wrap([1, 2, 3, 4] as byte[]))])
+        when:
+        byte b = 3;
+
+        then:
+        buf.indexOf(b) == 6
+
+        cleanup:
+        buf.release()
+    }
+
+    def "should not find any occurrence of a byte"() {
+        given:
+        def buf = new CompositeByteBuf([
+                new ByteBufNIO(ByteBuffer.wrap([5, 5, 5, 5] as byte[])),
+                new ByteBufNIO(ByteBuffer.wrap([1, 2, 3, 4] as byte[]))])
+
+        when:
+          byte b = 6;
+
+        then:
+        buf.indexOf(b) == -1
+
+        cleanup:
+        buf.release()
+    }
+
     def 'absolute getInt should read little endian integer and preserve position'() {
         given:
         def byteBuffer = new ByteBufNIO(ByteBuffer.wrap([1, 2, 3, 4] as byte[]))