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

org.eclipse.leshan.tlv.TlvEncoder Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2013-2015 Sierra Wireless and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * 
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *    http://www.eclipse.org/org/documents/edl-v10.html.
 * 
 * Contributors:
 *     Sierra Wireless - initial API and implementation
 *******************************************************************************/
package org.eclipse.leshan.tlv;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Date;

import org.eclipse.leshan.core.node.ObjectLink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TlvEncoder {

    private static final Logger LOG = LoggerFactory.getLogger(TlvEncoder.class);

    private static final int MAX_LENGTH_8BIT = 256;
    private static final int MAX_LENGTH_16BIT = 65_536;
    private static final int MAX_LENGTH_24BIT = 16_777_216;

    /**
     * Encodes an array of TLV.
     */
    public static ByteBuffer encode(Tlv[] tlvs) {
        int size = 0;

        LOG.trace("start");
        for (Tlv tlv : tlvs) {

            int length = tlvEncodedLength(tlv);
            size += tlvEncodedSize(tlv, length);
            LOG.trace("tlv size : {}", size);
        }
        LOG.trace("done, size : {}", size);
        ByteBuffer b = ByteBuffer.allocate(size);
        b.order(ByteOrder.BIG_ENDIAN);
        for (Tlv tlv : tlvs) {
            encode(tlv, b);
        }
        // HACK the cast is necessary for binary backward compatibility bug introduce in Java 9
        // https://github.com/apache/felix/pull/114
        ((Buffer) b).flip();
        return b;
    }

    /**
     * Encodes an integer value.
     */
    public static byte[] encodeInteger(Number number) {
        ByteBuffer iBuf;
        long lValue = number.longValue();
        if (lValue >= Byte.MIN_VALUE && lValue <= Byte.MAX_VALUE) {
            iBuf = ByteBuffer.allocate(1);
            iBuf.put((byte) lValue);
        } else if (lValue >= Short.MIN_VALUE && lValue <= Short.MAX_VALUE) {
            iBuf = ByteBuffer.allocate(2);
            iBuf.putShort((short) lValue);
        } else if (lValue >= Integer.MIN_VALUE && lValue <= Integer.MAX_VALUE) {
            iBuf = ByteBuffer.allocate(4);
            iBuf.putInt((int) lValue);
        } else {
            iBuf = ByteBuffer.allocate(8);
            iBuf.putLong(lValue);
        }
        return iBuf.array();
    }

    /**
     * Encodes a floating point value.
     */
    public static byte[] encodeFloat(Number number) {
        ByteBuffer fBuf;
        if (number instanceof Float) {
            fBuf = ByteBuffer.allocate(4);
            fBuf.putFloat(number.floatValue());
        } else {
            fBuf = ByteBuffer.allocate(8);
            fBuf.putDouble(number.doubleValue());
        }
        return fBuf.array();
    }

    /**
     * Encodes a boolean value.
     */
    public static byte[] encodeBoolean(boolean value) {
        return value ? new byte[] { 1 } : new byte[] { 0 };
    }

    /**
     * Encodes a string value.
     */
    public static byte[] encodeString(String value) {
        return value.getBytes(StandardCharsets.UTF_8);
    }

    /**
     * Encodes a date value.
     */
    public static byte[] encodeDate(Date value) {
        ByteBuffer tBuf = ByteBuffer.allocate(4);
        tBuf.putInt((int) (value.getTime() / 1000L));
        return tBuf.array();
    }

    /**
     * Encodes a Objlnk value.
     */
    public static byte[] encodeObjlnk(ObjectLink value) {
        ByteBuffer objlnkBuffer = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
        objlnkBuffer.putShort(0, (short) value.getObjectId());
        objlnkBuffer.putShort(2, (short) value.getObjectInstanceId());
        return objlnkBuffer.array();
    }

    private static int tlvEncodedSize(Tlv tlv, int length) {
        int size = 1 /* HEADER */;
        size += (tlv.getIdentifier() < MAX_LENGTH_8BIT) ? 1 : 2; /* 8 bits or 16 bits identifiers */

        if (length < 8) {
            size += 0;
        } else if (length < MAX_LENGTH_8BIT) {
            size += 1;
        } else if (length < MAX_LENGTH_16BIT) {
            size += 2;
        } else if (length < MAX_LENGTH_24BIT) {
            size += 3;
        } else {
            throw new IllegalArgumentException("length should fit in max 24bits");
        }

        size += length;
        return size;
    }

    private static int tlvEncodedLength(Tlv tlv) {
        int length;
        switch (tlv.getType()) {
        case RESOURCE_VALUE:
        case RESOURCE_INSTANCE:
            length = tlv.getValue().length;
            break;
        default:
            length = 0;
            for (Tlv child : tlv.getChildren()) {
                int subLength = tlvEncodedLength(child);
                length += tlvEncodedSize(child, subLength);
            }
        }

        return length;
    }

    private static void encode(Tlv tlv, ByteBuffer b) {
        int length;
        length = tlvEncodedLength(tlv);
        int typeByte;

        switch (tlv.getType()) {
        case OBJECT_INSTANCE:
            typeByte = 0b00_000000;
            break;
        case RESOURCE_INSTANCE:
            typeByte = 0b01_000000;
            break;
        case MULTIPLE_RESOURCE:
            typeByte = 0b10_000000;
            break;
        case RESOURCE_VALUE:
            // encode the value
            typeByte = 0b11_000000;
            break;
        default:
            throw new IllegalArgumentException("unknown TLV type : '" + tlv.getType() + "'");
        }

        // encode identifier length
        typeByte |= (tlv.getIdentifier() < MAX_LENGTH_8BIT) ? 0b00_0000 : 0b10_0000;

        // type of length
        if (length < 8) {
            typeByte |= length;
        } else if (length < MAX_LENGTH_8BIT) {
            typeByte |= 0b0000_1000;
        } else if (length < MAX_LENGTH_16BIT) {
            typeByte |= 0b0001_0000;
        } else {
            typeByte |= 0b0001_1000;
        }

        // fill the buffer
        b.put((byte) typeByte);
        if (tlv.getIdentifier() < MAX_LENGTH_8BIT) {
            b.put((byte) tlv.getIdentifier());
        } else {
            b.putShort((short) tlv.getIdentifier());
        }

        // write length

        if (length >= 8) {
            if (length < MAX_LENGTH_8BIT) {
                b.put((byte) length);
            } else if (length < MAX_LENGTH_16BIT) {
                b.putShort((short) length);
            } else {
                int msb = (length & 0xFF_00_00) >> 16;
                b.put((byte) msb);
                b.putShort((short) (length & 0xFF_FF));
                typeByte |= 0b0001_1000;
            }
        }

        switch (tlv.getType()) {
        case RESOURCE_VALUE:
        case RESOURCE_INSTANCE:
            b.put(tlv.getValue());
            break;
        default:
            for (Tlv child : tlv.getChildren()) {
                encode(child, b);
            }
            break;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy