AwdbReader.java

package com.xenoamess.ipplus360;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.xenoamess.ipplus360.entity.AwdbMetaData;
import com.xenoamess.ipplus360.enumerate.FileOpenMode;
import com.xenoamess.ipplus360.exception.InvalidAwdbException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * AWDB 文件读取器
 */
public class AwdbReader implements AutoCloseable {
    private static final Logger logger = LoggerFactory.getLogger(AwdbReader.class);
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private final AwdbBufferHolder bufferRef;
    private final AwdbNodeCache cache;
    private final AwdbMetaData awdbMetaData;
    /**
     * 4_6 混合库 ::ffff:0/96 前缀的预走结果(构造期计算一次);
     * 非 4_6 库为 -1。避免每次 v4 查询重走 96 位前缀。
     */
    private final long ipv4StartNode;

    /**
     * 私有构造函数,供内部使用
     */
    private AwdbReader(AwdbBufferHolder holder, AwdbNodeCache cache) throws IOException {
        this.bufferRef = holder;
        this.cache = cache;

        if (holder.isLargeFile()) {
            // 大文件解析逻辑
            LargeFileBuffer buffer = holder.getLargeFileBuffer();
            int metaLen = metaLengthLarge(buffer);
            AwdbDataParserLarge awdbDataParser = new AwdbDataParserLarge(this.cache, buffer);
            this.awdbMetaData = awdbDataParser.parseMeta(metaLen);
        } else {
            // 小文件解析逻辑 - 使用副本避免共享访问
            ByteBuffer buffer = holder.getByteBuffer();
            int metaLen = metaLength(buffer);
            AwdbDataParser awdbDataParser = new AwdbDataParser(this.cache, buffer.duplicate());
            this.awdbMetaData = awdbDataParser.parseMeta(metaLen);
        }

        if ("4_6".equals(this.awdbMetaData.getIpVersion())) {
            this.ipv4StartNode = prewalkIpv4Start();
        } else {
            this.ipv4StartNode = -1;
        }
    }

    /**
     * 预走 ::ffff:0/96 前缀(前 80 位 0 + 接下来 16 位 1),返回链尾节点索引
     */
    private long prewalkIpv4Start() throws IOException {
        AwdbBufferHolder holder = getBuffer();
        long nodeCount = awdbMetaData.getNodeCount();
        long nodeIndex = 0;
        if (holder.isLargeFile()) {
            LargeFileBuffer buffer = holder.getLargeFileBuffer();
            for (int i = 0; i < 80 && nodeIndex < nodeCount; i++) {
                nodeIndex = readNodeIndexLarge(buffer, nodeIndex, 0);
            }
            for (int i = 0; i < 16 && nodeIndex < nodeCount; i++) {
                nodeIndex = readNodeIndexLarge(buffer, nodeIndex, 1);
            }
        } else {
            ByteBuffer buffer = holder.getByteBuffer().duplicate();
            for (int i = 0; i < 80 && nodeIndex < nodeCount; i++) {
                nodeIndex = readNodeIndex(buffer, (int) nodeIndex, 0);
            }
            for (int i = 0; i < 16 && nodeIndex < nodeCount; i++) {
                nodeIndex = readNodeIndex(buffer, (int) nodeIndex, 1);
            }
        }
        return nodeIndex;
    }


    /**
     * 创建一个 AwdbReader 实例(完整参数)
     *
     * @param file  文件
     * @param cache 缓存实现
     * @param mode  文件打开模式
     * @return AwdbReader
     * @throws IOException IO异常
     */
    public static AwdbReader open(File file, AwdbNodeCache cache, FileOpenMode mode) throws IOException {
        return new AwdbReader(new AwdbBufferHolder(file, mode), cache);
    }

    /**
     * 元数据长度
     */
    private int metaLength(ByteBuffer buffer) {
        buffer.position(0);
        return buffer.getChar() & 0xFFFF; // 读取2字节作为长度
    }

    /**
     * 元数据长度(大文件)
     */
    private int metaLengthLarge(LargeFileBuffer buffer) {
        buffer.position(0);
        return buffer.getChar() & 0xFFFF; // 读取2字节作为长度
    }

    /**
     * 查询IP位置信息
     *
     * @param ipStr IP地址字符串
     * @return JSON结果
     */
    public JsonNode findIpLocation(String ipStr) throws IOException {
        if (ipStr == null || ipStr.isEmpty()) {
            return null;
        }

        try {
            return findIpLocation(InetAddress.getByName(ipStr.trim()));
        } catch (UnknownHostException e) {
            logger.warn("无法解析的IP地址: {}", ipStr, e);
            return null;
        }
    }

    /**
     * 查询IP位置信息
     *
     * @param ipAddr InetAddress
     * @return JSON结果
     */
    public JsonNode findIpLocation(InetAddress ipAddr) throws IOException {
        String ipVersion = awdbMetaData.getIpVersion();
        String v4 = "4";
        String v6 = "6";
        String mix = "4_6";

        if (v4.equals(ipVersion) && ipAddr.getAddress().length == 16) {
            return OBJECT_MAPPER.createObjectNode();
        }

        if (v6.equals(ipVersion) && ipAddr.getAddress().length == 4) {
            return OBJECT_MAPPER.createObjectNode();
        }

        long nodeIndex = 0;
        int startBit = 0;
        byte[] rawAddr = ipAddr.getAddress();
        if (mix.equals(ipVersion)) {
            if (rawAddr.length == 4) {
                // 处理IPv4地址转换:直接构造 ::ffff:a.b.c.d 的16字节形式。
                // 不能经 InetAddress 往返——现代 JDK 会把 mapped 地址归一化回
                // Inet4Address(getByName 和 getByAddress 均如此),导致位序错乱。
                byte[] v6mapped = new byte[16];
                v6mapped[10] = (byte) 0xFF;
                v6mapped[11] = (byte) 0xFF;
                System.arraycopy(rawAddr, 0, v6mapped, 12, 4);
                rawAddr = v6mapped;
                // 前96位前缀已在构造期预走(ipv4StartNode),只搜索后面32位
                nodeIndex = ipv4StartNode;
                startBit = 96;
            }
        }


        nodeIndex = findTreeIndex(rawAddr, nodeIndex, startBit);
        if (nodeIndex <= 0) {
            return OBJECT_MAPPER.createObjectNode();
        }

        long pointer = awdbMetaData.getBaseOffset() + nodeIndex - awdbMetaData.getNodeCount() - 10;
        switch (awdbMetaData.getDecodeType()) {
            case 1:
                JsonNode structureResult = decodeContentStructure(pointer);
                if (structureResult == null) {
                    return OBJECT_MAPPER.createObjectNode();
                }
                // 原始数组记录在此做 key-value 映射,两个入口 API 返回结构一致
                if (structureResult.isArray()) {
                    return mapKeyValue(awdbMetaData.getColumns(), structureResult);
                }
                return structureResult;
            case 2:
                try {
                    return decodeContentDirect(pointer);
                } catch (Exception e) {
                    logger.warn("解码失败,offset={}", pointer, e);
                    return OBJECT_MAPPER.createObjectNode();
                }
            default:
                return new TextNode("Invalid decode type: " + awdbMetaData.getDecodeType());
        }
    }

    /**
     * 查找树形索引
     */
    private long findTreeIndex(byte[] rawAddr, long nodeIndex, int startBit) throws IOException {
        AwdbBufferHolder holder = getBuffer();

        int bitLength = rawAddr.length * 8;
        long nodeCount = awdbMetaData.getNodeCount();

        if (holder.isLargeFile()) {
            LargeFileBuffer buffer = holder.getLargeFileBuffer(); // getLargeFileBuffer() 已自动返回独立副本
            for (int pl = startBit; pl < bitLength && nodeIndex < nodeCount; pl++) {
                int b = 0xFF & rawAddr[pl / 8];
                int bit = 1 & (b >> 7 - (pl % 8));
                nodeIndex = readNodeIndexLarge(buffer, nodeIndex, bit);
            }
        } else {
            // 使用 ByteBuffer 副本避免共享访问
            ByteBuffer buffer = holder.getByteBuffer().duplicate();
            for (int pl = startBit; pl < bitLength && nodeIndex < nodeCount; pl++) {
                int b = 0xFF & rawAddr[pl / 8];
                int bit = 1 & (b >> 7 - (pl % 8));
                nodeIndex = readNodeIndex(buffer, (int) nodeIndex, bit);
            }
        }

        if (nodeIndex == nodeCount) {
            return 0;
        } else if (nodeIndex > nodeCount) {
            return nodeIndex;
        } else {
            return 0;
        }
    }

    /**
     * 读取节点索引(小文件)
     */
    private int readNodeIndex(ByteBuffer buffer, int nodeIndex, int bit) throws IOException {
        // 用 long 计算偏移:文件接近 2GB 时 nodeIndex*byteLen*2 按 int 会溢出为负
        long offset = (long) nodeIndex * awdbMetaData.getByteLen() * 2
                + (long) bit * awdbMetaData.getByteLen()
                + awdbMetaData.getStartLength();
        if (offset < 0 || offset + awdbMetaData.getByteLen() > buffer.capacity()) {
            throw new InvalidAwdbException("Node offset " + offset + " with length "
                    + awdbMetaData.getByteLen() + " out of bounds [0, " + buffer.capacity() + "]");
        }
        // 创建buffer副本,设置offset,不修改原buffer的position,线程安全
        ByteBuffer bufferCopy = buffer.duplicate();
        bufferCopy.position((int) offset);
        return AwdbDataParser.buffer2Integer(bufferCopy, 0, awdbMetaData.getByteLen());
    }

    /**
     * 读取节点索引(大文件)
     */
    private long readNodeIndexLarge(LargeFileBuffer buffer, long nodeIndex, int bit) throws IOException {
        int byteLen = awdbMetaData.getByteLen();
        long startLength = awdbMetaData.getStartLength();
        long offset = nodeIndex * byteLen * 2L + bit * byteLen + startLength;
        if (offset < 0 || offset + byteLen > buffer.capacity()) {
            throw new IOException("Offset " + offset + " with length " + byteLen + " exceeds buffer capacity " + buffer.capacity());
        }
        byte[] bytes = new byte[byteLen];
        // 绝对位置读取,不需要设置buffer的position,完全线程安全
        buffer.get(offset, bytes, 0, byteLen);

        long unsignedResult = 0;
        for (int i = 0; i < bytes.length; i++) {
            unsignedResult = (unsignedResult << 8) | (bytes[i] & 0xFF);
        }
        
        return unsignedResult;
    }

    /**
     * 结构解码
     */
    private JsonNode decodeContentStructure(long offset) throws IOException {
        AwdbBufferHolder holder = getBuffer();
        try {
            if (holder.isLargeFile()) {
                LargeFileBuffer buffer = holder.getLargeFileBuffer(); // getLargeFileBuffer() 已自动返回独立副本
                AwdbDataParserLarge parser = new AwdbDataParserLarge(cache, buffer, awdbMetaData.getBaseOffset());
                return parser.parseData(offset);
            } else {
                // 使用 ByteBuffer 副本避免共享访问
                ByteBuffer buffer = holder.getByteBuffer().duplicate();
                AwdbDataParser parser = new AwdbDataParser(cache, buffer, awdbMetaData.getBaseOffset());
                return parser.parseData((int) offset);
            }
        } catch (Exception e) {
            logger.warn("解码失败,offset={}", offset, e);
            return null;
        }
    }

    /**
     * 直接解码
     */
    private JsonNode decodeContentDirect(long offset) throws IOException {
        AwdbBufferHolder holder = getBuffer();
        if (holder.isLargeFile()) {
            return decodeContentDirectLarge(offset, holder.getLargeFileBuffer());
        } else {
            return decodeContentDirectSmall(offset, holder.getByteBuffer());
        }
    }

    /**
     * 直接解码(小文件)
     */
    private JsonNode decodeContentDirectSmall(long offset, ByteBuffer buffer) throws IOException {
        // 使用副本做顺序读取,兼容无底层数组的 MappedByteBuffer
        ByteBuffer bufferCopy = buffer.duplicate();
        bufferCopy.position((int) offset);

        int dataLen = AwdbDataParser.buffer2Integer(bufferCopy, 0, 4);
        if (dataLen < 0 || dataLen > bufferCopy.remaining()) {
            throw new InvalidAwdbException("Invalid data length " + dataLen + " at offset " + offset);
        }
        byte[] data = new byte[dataLen];
        bufferCopy.get(data);
        String[] values = new String(data, "UTF-8").split("\t");

        Map<String, JsonNode> result = new HashMap<>(awdbMetaData.getColumns().size());
        for (int i = 0; i < awdbMetaData.getColumns().size(); i++) {
            String value = values.length - i > 0 ? values[i] : "";
            result.put((String) awdbMetaData.getColumns().get(i), new TextNode(value));
        }

        return new ObjectNode(OBJECT_MAPPER.getNodeFactory(), Collections.unmodifiableMap(result));
    }

    /**
     * 直接解码(大文件)
     */
    private JsonNode decodeContentDirectLarge(long offset, LargeFileBuffer buffer) throws IOException {
        long position = buffer.position();
        try {
            buffer.position(offset);

            byte[] lenBytes = new byte[4];
            buffer.get(lenBytes);
            int dataLen = (lenBytes[0] << 24) | ((lenBytes[1] & 0xFF) << 16) | ((lenBytes[2] & 0xFF) << 8) | (lenBytes[3] & 0xFF);
            if (dataLen < 0 || dataLen > buffer.remaining()) {
                throw new InvalidAwdbException("Invalid data length " + dataLen + " at offset " + offset);
            }

            byte[] dataBytes = new byte[dataLen];
            buffer.get(dataBytes);
            String[] values = new String(dataBytes, "UTF-8").split("\t");

            Map<String, JsonNode> result = new HashMap<>(awdbMetaData.getColumns().size());
            for (int i = 0; i < awdbMetaData.getColumns().size(); i++) {
                String value = values.length - i > 0 ? values[i] : "";
                result.put((String) awdbMetaData.getColumns().get(i), new TextNode(value));
            }

            return new ObjectNode(OBJECT_MAPPER.getNodeFactory(), Collections.unmodifiableMap(result));
        } finally {
            buffer.position(position);
        }
    }

    /**
     * 全量遍历回调
     */
    public interface WalkCallback {
        /**
         * 每命中一个(前缀, 记录)对调用一次。
         *
         * @param address      前缀地址字节(32 位树为 4 字节,128 位树为 16 字节),回调持有副本
         * @param prefixLength 前缀位数(0 = 全空间)
         * @param record       记录(与 findIpLocation 返回结构一致:decode1/2 均为映射对象)
         * @throws IOException 读取失败
         */
        void onNetwork(byte[] address, int prefixLength, JsonNode record) throws IOException;
    }

    /**
     * 深度优先遍历整棵前缀树,按(前缀, 记录)汇报。
     * 空子树跳过;同一节点的两个子节点为相同叶子值时合并到当前深度汇报。
     * 记录按叶子值去重解码。
     *
     * @param callback 遍历回调
     * @throws IOException 读取失败
     */
    public void walk(WalkCallback callback) throws IOException {
        int bitLength = "4".equals(awdbMetaData.getIpVersion()) ? 32 : 128;
        byte[] address = new byte[bitLength / 8];
        AwdbBufferHolder holder = getBuffer();
        Map<Long, JsonNode> recordCache = new HashMap<>();
        if (holder.isLargeFile()) {
            walkNodeLarge(holder.getLargeFileBuffer(), 0, address, 0, callback, recordCache);
        } else {
            walkNodeSmall(holder.getByteBuffer(), 0, address, 0, callback, recordCache);
        }
    }

    private void walkNodeSmall(ByteBuffer buffer, long nodeIndex, byte[] address, int depth,
                               WalkCallback callback, Map<Long, JsonNode> recordCache) throws IOException {
        long nodeCount = awdbMetaData.getNodeCount();
        long left = readNodeIndex(buffer, (int) nodeIndex, 0);
        long right = readNodeIndex(buffer, (int) nodeIndex, 1);
        if (left == right && left > nodeCount) {
            emitRecord(left, address, depth, callback, recordCache);
            return;
        }
        walkChildSmall(buffer, left, address, depth, 0, callback, recordCache);
        walkChildSmall(buffer, right, address, depth, 1, callback, recordCache);
    }

    private void walkChildSmall(ByteBuffer buffer, long child, byte[] address, int depth, int bit,
                                WalkCallback callback, Map<Long, JsonNode> recordCache) throws IOException {
        long nodeCount = awdbMetaData.getNodeCount();
        if (child == nodeCount) {
            return;
        }
        setAddressBit(address, depth, bit);
        if (child > nodeCount) {
            emitRecord(child, address, depth + 1, callback, recordCache);
        } else {
            walkNodeSmall(buffer, child, address, depth + 1, callback, recordCache);
        }
        clearAddressBit(address, depth);
    }

    private void walkNodeLarge(LargeFileBuffer buffer, long nodeIndex, byte[] address, int depth,
                               WalkCallback callback, Map<Long, JsonNode> recordCache) throws IOException {
        long nodeCount = awdbMetaData.getNodeCount();
        long left = readNodeIndexLarge(buffer, nodeIndex, 0);
        long right = readNodeIndexLarge(buffer, nodeIndex, 1);
        if (left == right && left > nodeCount) {
            emitRecord(left, address, depth, callback, recordCache);
            return;
        }
        walkChildLarge(buffer, left, address, depth, 0, callback, recordCache);
        walkChildLarge(buffer, right, address, depth, 1, callback, recordCache);
    }

    private void walkChildLarge(LargeFileBuffer buffer, long child, byte[] address, int depth, int bit,
                                WalkCallback callback, Map<Long, JsonNode> recordCache) throws IOException {
        long nodeCount = awdbMetaData.getNodeCount();
        if (child == nodeCount) {
            return;
        }
        setAddressBit(address, depth, bit);
        if (child > nodeCount) {
            emitRecord(child, address, depth + 1, callback, recordCache);
        } else {
            walkNodeLarge(buffer, child, address, depth + 1, callback, recordCache);
        }
        clearAddressBit(address, depth);
    }

    private void emitRecord(long leaf, byte[] address, int prefixLength,
                            WalkCallback callback, Map<Long, JsonNode> recordCache) throws IOException {
        JsonNode record = recordCache.get(leaf);
        if (record == null && !recordCache.containsKey(leaf)) {
            record = decodeLeafRecord(leaf);
            recordCache.put(leaf, record);
        }
        if (record != null) {
            callback.onNetwork(Arrays.copyOf(address, address.length), prefixLength, record);
        }
    }

    /**
     * 按叶子值解码记录,返回结构与 findIpLocation 一致
     */
    private JsonNode decodeLeafRecord(long leaf) throws IOException {
        long pointer = awdbMetaData.getBaseOffset() + leaf - awdbMetaData.getNodeCount() - 10;
        switch (awdbMetaData.getDecodeType()) {
            case 1:
                JsonNode structureResult = decodeContentStructure(pointer);
                if (structureResult != null && structureResult.isArray()) {
                    return mapKeyValue(awdbMetaData.getColumns(), structureResult);
                }
                return structureResult;
            case 2:
                try {
                    return decodeContentDirect(pointer);
                } catch (Exception e) {
                    logger.warn("遍历时解码失败,offset={}", pointer, e);
                    return null;
                }
            default:
                throw new InvalidAwdbException("Invalid decode type: " + awdbMetaData.getDecodeType());
        }
    }

    private static void setAddressBit(byte[] address, int bitIndex, int bit) {
        if (bit == 1) {
            address[bitIndex / 8] |= (byte) (0x80 >> (bitIndex % 8));
        }
    }

    private static void clearAddressBit(byte[] address, int bitIndex) {
        address[bitIndex / 8] &= (byte) ~(0x80 >> (bitIndex % 8));
    }

    /**
     * 获取缓冲区
     */
    private AwdbBufferHolder getBuffer() {
        return bufferRef;
    }

    /**
     * key和value映射
     *
     * @param keys   key列表
     * @param values value列表
     * @return JsonNode对象
     */
    private JsonNode mapKeyValue(List<Object> keys, JsonNode values) {
        Map<String, JsonNode> resultDict = new HashMap<>(keys.size());

        if (keys.size() == values.size()) {
            for (int i = 0; i < keys.size(); i++) {
                resultDict.put((String) keys.get(i), values.get(i));
            }
        } else {
            for (int i = 0; i < values.size() - 1; i++) {
                resultDict.put((String) keys.get(i), values.get(i));
            }

            String multiAreasName = (String) keys.get(keys.size() - 2);
            List<?> keysList = (List<?>) keys.get(keys.size() - 1);
            JsonNode valuesJsonNode = values.get(values.size() - 1);

            ArrayNode nodes = new ArrayNode(OBJECT_MAPPER.getNodeFactory());
            for (JsonNode value : valuesJsonNode) {
                Map<String, JsonNode> tempDic = new HashMap<>(keysList.size());

                for (int i = 0; i < keysList.size(); i++) {
                    tempDic.put((String) keysList.get(i), value.get(i));
                }
                nodes.add(new ObjectNode(OBJECT_MAPPER.getNodeFactory(), Collections.unmodifiableMap(tempDic)));
            }
            resultDict.put(multiAreasName, nodes);
        }

        return new ObjectNode(OBJECT_MAPPER.getNodeFactory(), Collections.unmodifiableMap(resultDict));
    }



    @Override
    public void close() throws IOException {
        if (bufferRef != null) {
            bufferRef.close();
        }
    }

    /**
     * 获取AWDB文件的元数据
     */
    public AwdbMetaData getAwdbMetaData() {
        return awdbMetaData;
    }
}