class RemoteDigestBlob {

    public enum Status {
        FULL((byte) 0),
        PARTIAL((byte) 1),
        MISMATCH((byte) 2),
        EXISTS((byte) 3),
        FAILED((byte) 4);


        private final byte id;

        Status(byte id) {
            this.id = id;
        }

        /**
         * The internal representation of the status.
         */
        public byte id() {
            return id;
        }

        public static Status fromId(byte id) {
            switch (id) {
                case 0:
                    return FULL;
                case 1:
                    return PARTIAL;
                case 2:
                    return MISMATCH;
                case 3:
                    return EXISTS;
                case 4:
                    return FAILED;
                default:
                    throw new IllegalArgumentException("No status match for [" + id + "]");
            }
        }
    }


    private static final Logger LOGGER = LogManager.getLogger(RemoteDigestBlob.class);

    private final ShardId shardId;
    private final String digest;
    private final Client client;
    private long size;
    private StartBlobResponse startResponse;
    private UUID transferId;
    private Status status;


    public RemoteDigestBlob(Client client, ShardId shardId, String digest) {
        this.digest = digest;
        this.client = client;
        this.size = 0;
        this.shardId = shardId;
    }

    public Status status() {
        return status;
    }

    public boolean delete() {
        LOGGER.trace("delete");
        assert transferId == null : "transferId should be null";
        DeleteBlobRequest request = new DeleteBlobRequest(
            shardId,
            Hex.decodeHex(digest)
        );
        return FutureUtils.get(client.execute(DeleteBlobAction.INSTANCE, request)).deleted;
    }

    private Status start(ByteBuf buffer, boolean last) {
        LOGGER.trace("start blob upload");
        assert transferId == null : "transferId should be null";
        StartBlobRequest request = new StartBlobRequest(
            shardId,
            Hex.decodeHex(digest),
            Netty4Utils.toBytesReference(buffer),
            last
        );
        transferId = request.transferId();
        size += buffer.readableBytes();

        startResponse = FutureUtils.get(client.execute(StartBlobAction.INSTANCE, request));
        status = startResponse.status();
        return status;
    }

    private Status chunk(ByteBuf buffer, boolean last) {
        assert transferId != null : "transferId should not be null";
        PutChunkRequest request = new PutChunkRequest(
            shardId,
            Hex.decodeHex(digest),
            transferId,
            Netty4Utils.toBytesReference(buffer),
            size,
            last
        );
        size += buffer.readableBytes();
        PutChunkResponse putChunkResponse = FutureUtils.get(client.execute(PutChunkAction.INSTANCE, request));
        return putChunkResponse.status();
    }

    public Status addContent(ByteBuf buffer, boolean last) {<FILL_FUNCTION_BODY>}

    public long size() {
        return size;
    }
}

        if (startResponse == null) {
            // this is the first call to addContent
            return start(buffer, last);
        } else if (status == Status.EXISTS) {
            // client probably doesn't support 100-continue and is sending chunked requests
            // need to ignore the content.
            return status;
        } else if (status != Status.PARTIAL) {
            throw new IllegalStateException("Expected Status.PARTIAL for chunk but got: " + status);
        } else {
            return chunk(buffer, last);
        }
    
<no_super_class>