int fillFrom(ByteBuf channelReadBuf) {
      int toWrite;
      ByteBuf slice;
      if (buf == null) {
        // If current buf is null, then only read the up to routerMaxPutChunkSizeBytes.
        toWrite = Math.min(channelReadBuf.readableBytes(), routerConfig.routerMaxPutChunkSizeBytes);
        slice = channelReadBuf.readRetainedSlice(toWrite);
        buf = slice;
        buf.touch(loggingContext);
      } else {
        int remainingSize = routerConfig.routerMaxPutChunkSizeBytes - buf.readableBytes();
        toWrite = Math.min(channelReadBuf.readableBytes(), remainingSize);
        slice = channelReadBuf.readRetainedSlice(toWrite);
        slice.touch(loggingContext);
        // buf already has some bytes
        if (buf instanceof CompositeByteBuf) {
          // Buf is already a CompositeByteBuf, then just add the slice from
          ((CompositeByteBuf) buf).addComponent(true, slice);
        } else {
          int maxComponents = routerConfig.routerMaxPutChunkSizeBytes;
          CompositeByteBuf composite = buf.isDirect() ? buf.alloc().compositeDirectBuffer(maxComponents)
              : buf.alloc().compositeHeapBuffer(maxComponents);
          composite.addComponents(true, buf, slice);
          buf = composite;
          buf.touch(loggingContext);
        }
      }

      // Update crc for the chunk data
      if (routerConfig.routerVerifyCrcForPutRequests) {
        chunkCrc32.update(slice.nioBuffer());
      }

      if (buf.readableBytes() == routerConfig.routerMaxPutChunkSizeBytes) {
        if (chunkIndex == 0) {
          // If first put chunk is full, but not yet prepared then mark it awaiting resolution instead of completing it.
          // This chunk will be prepared as soon as either more bytes are read from the channel, or the chunk filling
          // is complete. At that point we will have enough information to mark this blob as simple or composite blob.
          state = ChunkState.AwaitingBlobTypeResolution;
        } else {
          onFillComplete(true);
        }
      }
      return toWrite;
    }
Fill the buffer of the current chunk with the data from the given {@link ByteBuf}.
@param channelReadBuf the {@link ByteBuf} from which to read data.
@return the number of bytes transferred in this operation.
ambry-router/src/main/java/com/github/ambry/router/PutOperation.java