aboutsummaryrefslogtreecommitdiffstats
path: root/document/src/main/java/com/yahoo/document/BucketId.java
blob: af94a0e7683470417157c43c45237f69a420ac8b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document;

/**
 * Representation of a bucket identifier.
 */
public class BucketId implements Comparable<BucketId> {

    public static final int COUNT_BITS = 6;
    private long id = 0;
    private final static long[] usedMask;

    static {
        usedMask = new long[59];
        long val = 0;
        for (int i=0; i<usedMask.length; ++i) {
            usedMask[i] = val;
            val = (val << 1) | 1;
        }
    }

    /** Default-constructed BucketId signifies an invalid bucket ID. */
    public BucketId() {
    }

    /**
     * Creates a bucket id with the given raw bucket id. This is a 64 bit mask
     * where the first 6 MSB bits set how many LSB bits should actually be used.
     * Right now it only have simple functionality. More will be added for it
     * to be configurable.
     */
    public BucketId(long id) {
        this.id = id;
    }

    public BucketId(int usedBits, long id) {
        long usedMask = ((long) usedBits) << (64 - COUNT_BITS);
        id <<= COUNT_BITS;
        id >>>= COUNT_BITS;
        this.id = id | usedMask;
    }

    public BucketId(String serialized) {
        if (!serialized.startsWith("BucketId(0x")) {
            throw new IllegalArgumentException("Serialized bucket id must start with 'BucketId(0x'");
        }
        if (!serialized.endsWith(")")) {
            throw new IllegalArgumentException("Serialized bucket id must end with ')'");
        }

        // Parse hex string after "0x"
        int index;
        char c;
        long id = 0;
        for (index = 11; index < serialized.length()-1; index++) {
            c = serialized.charAt(index);
            if (!((c>=48 && c<=57) || // digit
                  (c>=97 && c<=102))) { // a-f
                throw new IllegalArgumentException("Serialized bucket id (" + serialized + ") contains illegal character at position " + index);
            }
            id <<= 4;
            id += Integer.parseInt(String.valueOf(c),16);
        }
        this.id = id;
        if (getUsedBits() == 0) {
            throw new IllegalArgumentException("Created bucket id "+id+", but no countbits are set");
        }
    }

    public boolean equals(Object o) {
        return (o instanceof BucketId && ((BucketId) o).getId() == this.getId());
    }

    public int compareTo(BucketId other) {
        if (id >>> 32 == other.id >>> 32) {
            if ((id & 0xFFFFFFFFL) > (other.id & 0xFFFFFFFFL)) {
                return 1;
            } else if ((id & 0xFFFFFFFFL) < (other.id & 0xFFFFFFFFL)) {
                return -1;
            }
            return 0;
        } else if ((id >>> 32) > (other.id >>> 32)) {
            return 1;
        } else {
            return -1;
        }
    }

    public int hashCode() {
        return (int) id;
    }

    public int getUsedBits() { return (int) (id >>> (64 - COUNT_BITS)); }

    public long getRawId() { return id; }

    public long getId() {
        int notUsed = 64 - getUsedBits();
        long usedMask  = (0xFFFFFFFFFFFFFFFFL << notUsed) >>> notUsed;
        long countMask = (0xFFFFFFFFFFFFFFFFL >>> (64 - COUNT_BITS)) << (64 - COUNT_BITS);
        return id & (usedMask | countMask);
    }

    public long withoutCountBits() {
        return id & usedMask[getUsedBits()];
    }

    public String toString() {
        StringBuilder sb = new StringBuilder().append("BucketId(0x");
        String number = Long.toHexString(getId());
        for (int i=number.length(); i<16; ++i) {
            sb.append('0');
        }
        sb.append(number).append(')');
        return sb.toString();
    }

    public boolean contains(BucketId id) {
        if (id.getUsedBits() < getUsedBits()) {
            return false;
        }
        BucketId copy = new BucketId(getUsedBits(), id.getRawId());
        return (copy.getId() == getId());
    }

    public boolean contains(DocumentId docId, BucketIdFactory factory) {
        return contains(factory.getBucketId(docId));
    }

}