All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.document.GlobalId Maven / Gradle / Ivy

// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document;

import com.yahoo.collections.MD5;
import com.yahoo.document.idstring.IdString;
import com.yahoo.vespa.objects.Deserializer;
import com.yahoo.vespa.objects.Serializer;

import java.util.Arrays;

/**
 * Implements an incredibly light-weight version of the document global id. There is a lot of functionality in the C++
 * version of this that is missing. However, this should be sufficient for now.
 *
 * This is immutable (by contract - not enforcable due to exposing the raw byte array).
 *
 * @author Simon Thoresen Hult
 */
public class GlobalId implements Comparable {

    /**
     * The number of bytes in a global id. This must match the C++ constant in "document/base/globalid.h".
     */
    public static final int LENGTH = 12;

    // The raw bytes that constitutes this global id.
    private final byte[] raw;

    /**
     * Constructs a new global id by copying the content of the given raw byte array.
     *
     * @param raw The array to copy.
     */
    public GlobalId(byte[] raw) {
        this.raw = new byte [12];
        int len = Math.min(LENGTH, raw.length);
        System.arraycopy(raw, 0, this.raw, 0, len);
    }

    /**
     * Constructs a new global id from a document id string.
     *
     * @param id the document id to derive from
     */
    public GlobalId(IdString id) {
        byte[] raw = MD5.md5.get().digest(id.toUtf8().wrap().array());
        long location = id.getLocation();
        this.raw = new byte [LENGTH];
        for (int i = 0; i < 4; ++i) {
            this.raw[i] = (byte)((location >> (8 * i)) & 0xFF);
        }
        for (int i=4; i < LENGTH; i++) {
            this.raw[i] = raw[i];
        }
    }

    /**
     * Constructs a global id by deserializing content from the given byte buffer.
     *
     * @param buf the buffer to deserialize from
     */
    public GlobalId(Deserializer buf) {
        raw = buf.getBytes(null, LENGTH);
    }

    /**
     * Serializes the content of this global id into the given byte buffer.
     *
     * @param buf the buffer to serialize to
     */
    public void serialize(Serializer buf) {
        buf.put(null, raw);
    }

    /**
     * Returns the raw byte array that constitutes this global id.
     * The returned value MUST NOT be modified.
     */
    public byte[] getRawId() {
        return raw;
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(raw);
    }

    public BucketId toBucketId() {
        /*
         * Explanation time: since Java was designed so mankind could suffer,
         * shift ops on bytes have an implicit int conversion with sign-extend.
         * When a byte is negative, you end up with an int/long with a 0xFFFFFF
         * prefix, in turn causing your other friendly bitwise ORs to act
         * pretty far from what was originally intended.
         * To get around this, we explicitly sign extend before the compiler can
         * do so for us and make sure to OR away any sign extensions.
         */
        long location = ((long)raw[0] & 0xFF)
                | (((long)raw[1] & 0xFF) << 8)
                | (((long)raw[2] & 0xFF) << 16)
                | (((long)raw[3] & 0xFF) << 24);
        long md5 = 0;
        for (int i = 4, j = 0; i < LENGTH; i++, j += 8) {
            md5 |= ((long)raw[i] & 0xFF) << j;
        }
        // Drumroll: this is why 'location' is of type long. Otherwise, the
        // ORing would sign-extend it and cause havoc when its MSB is set.
        long rawBucketId = (md5 & 0xFFFFFFFF00000000L) | location;
        return new BucketId(58, rawBucketId);
    }

    // Inherit doc from Object.
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof GlobalId)) {
            return false;
        }
        GlobalId rhs = (GlobalId) obj;
        return Arrays.equals(raw, rhs.raw);
    }

    @Override
    public int compareTo(GlobalId other) {
        for (int i=0 ; i otherByte) {
                return 1;
            }
        }
        return 0;
    }

    @Override
    public String toString() {
        StringBuilder strb = new StringBuilder(50);
        for (byte b : raw) {
            strb.append(" ").append(0xFF & (int) b);
        }
        return strb.toString().trim();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy