/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cassandra.serializers; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.Duration; import org.apache.cassandra.io.util.DataInputBuffer; import org.apache.cassandra.io.util.DataOutputBufferFixed; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.vint.VIntCoding; public final class DurationSerializer implements TypeSerializer<Duration> { public static final DurationSerializer instance = new DurationSerializer(); public ByteBuffer serialize(Duration duration) { if (duration == null) return ByteBufferUtil.EMPTY_BYTE_BUFFER; long months = duration.getMonths(); long days = duration.getDays(); long nanoseconds = duration.getNanoseconds(); int size = VIntCoding.computeVIntSize(months) + VIntCoding.computeVIntSize(days) + VIntCoding.computeVIntSize(nanoseconds); try (DataOutputBufferFixed output = new DataOutputBufferFixed(size)) { output.writeVInt(months); output.writeVInt(days); output.writeVInt(nanoseconds); return output.buffer(); } catch (IOException e) { // this should never happen with a DataOutputBufferFixed throw new AssertionError("Unexpected error", e); } } public Duration deserialize(ByteBuffer bytes) { if (bytes.remaining() == 0) return null; try (DataInputBuffer in = new DataInputBuffer(bytes, true)) { int months = (int) in.readVInt(); int days = (int) in.readVInt(); long nanoseconds = in.readVInt(); return Duration.newInstance(months, days, nanoseconds); } catch (IOException e) { // this should never happen with a DataInputBuffer throw new AssertionError("Unexpected error", e); } } public void validate(ByteBuffer bytes) throws MarshalException { if (bytes.remaining() < 3) throw new MarshalException(String.format("Expected at least 3 bytes for a duration (%d)", bytes.remaining())); try (DataInputBuffer in = new DataInputBuffer(bytes, true)) { long monthsAsLong = in.readVInt(); long daysAsLong = in.readVInt(); long nanoseconds = in.readVInt(); if (!canBeCastToInt(monthsAsLong)) throw new MarshalException(String.format("The duration months must be a 32 bits integer but was: %d", monthsAsLong)); if (!canBeCastToInt(daysAsLong)) throw new MarshalException(String.format("The duration days must be a 32 bits integer but was: %d", daysAsLong)); int months = (int) monthsAsLong; int days = (int) daysAsLong; if (!((months >= 0 && days >= 0 && nanoseconds >= 0) || (months <= 0 && days <=0 && nanoseconds <=0))) throw new MarshalException(String.format("The duration months, days and nanoseconds must be all of the same sign (%d, %d, %d)", months, days, nanoseconds)); } catch (IOException e) { // this should never happen with a DataInputBuffer throw new AssertionError("Unexpected error", e); } } /** * Checks that the specified {@code long} can be cast to an {@code int} without information lost. * * @param l the {@code long} to check * @return {@code true} if the specified {@code long} can be cast to an {@code int} without information lost, * {@code false} otherwise. */ private boolean canBeCastToInt(long l) { return ((int) l) == l; } public String toString(Duration duration) { return duration == null ? "" : duration.toString(); } public Class<Duration> getType() { return Duration.class; } }