/**
* Copyright 2016 LinkedIn Corp. All rights reserved.
*
* 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.
*/
package com.github.ambry.store;
import com.github.ambry.utils.Utils;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
/**
* Denotes an offset inside the log.
*/
public class Offset implements Comparable<Offset> {
private final String name;
private final long offset;
private static final short CURRENT_VERSION = 0;
private static final int VERSION_SIZE = 2;
private static final int NAME_LENGTH_SIZE = 4;
private static final int OFFSET_SIZE = 8;
/**
* Construct an Offset that refers to a position in Log.
* @param name the name of segment being referred to.
* @param offset the offset within the segment.
* @throws IllegalArgumentException if {@code name} is {@code null} or {@code offset} < 0.
*/
Offset(String name, long offset) {
if (name == null || offset < 0) {
throw new IllegalArgumentException("Name [" + name + "] is null or offset [" + offset + "] < 0");
}
this.name = name;
this.offset = offset;
}
/**
* Constructs an Offset from a stream.
* @param stream the {@link DataInputStream} that will contain the serialized form of this object.
* @throws IllegalArgumentException if {@code name} is {@code null} or {@code offset} < 0 or if the version
* of the record is not recognized.
* @throws IOException if there are I/O problems reading from the stream.
*/
public static Offset fromBytes(DataInputStream stream) throws IOException {
String name;
long offset;
int version = stream.readShort();
switch (version) {
case 0:
name = Utils.readIntString(stream, StandardCharsets.UTF_8);
offset = stream.readLong();
break;
default:
throw new IllegalArgumentException("Unrecognized version [" + version + "] of Offset");
}
return new Offset(name, offset);
}
/**
* @return the name of the log segment for which the offset provided by {@link #getOffset()} is valid. Guaranteed to
* be non-null and non-empty.
*/
public String getName() {
return name;
}
/**
* @return the offset in the log segment with name provided by {@link #getName()}. Guaranteed to be >= 0.
*/
public long getOffset() {
return offset;
}
/**
* Converts this object into its byte representation.
* @return the byte representation of this object.
*/
byte[] toBytes() {
byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
int size = VERSION_SIZE + NAME_LENGTH_SIZE + nameBytes.length + OFFSET_SIZE;
byte[] bytes = new byte[size];
ByteBuffer buf = ByteBuffer.wrap(bytes);
buf.putShort(CURRENT_VERSION);
buf.putInt(nameBytes.length);
buf.put(nameBytes);
buf.putLong(offset);
return bytes;
}
@Override
public int compareTo(Offset o) {
int compare = LogSegmentNameHelper.COMPARATOR.compare(name, o.name);
return compare == 0 ? Long.compare(offset, o.offset) : compare;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Offset other = (Offset) o;
return compareTo(other) == 0;
}
@Override
public int hashCode() {
int result = LogSegmentNameHelper.hashcode(name);
result = 31 * result + (int) (offset ^ (offset >>> 32));
return result;
}
@Override
public String toString() {
return "Name = [" + name + "] Offset = [" + offset + "]";
}
}