# Welcome

Welcome to the IP NEWS Tutorial Center! This document will help you get started quickly with our interactive documentation.

{% content-ref url="/pages/Zvukm7MAGL6ooitMTmpY" %}
[Web Services Documentation](/web-services-documentation/quickstart)
{% endcontent-ref %}

{% content-ref url="/pages/7aUFmnCMx9m4smGncsXL" %}
[Database Documentation](/database-documentation/editor)
{% endcontent-ref %}

If you don't find a tutorial for your problem or need help, please contact us via email: **<support@ipnews.com>**


# Overview

ipnews.io provides RESTful API allowing users to check IP address location in real time. The REST API supports both IPv4 and IPv6 address lookup.

You can get the following data:\
Country, Region, City, Latitude, Longitude, ISP, Time Zone, Area Code, Postal Code, Weather Station Name, Weather Station Code, Mobile Operator, Altitude, Currency, EU, Country Capital, Radius, Threat Data, Usage Type, Number of IP-Associated Domains, IP-Associated Domain Names, ASN, ASN Organization Domain, ASN Organization Name, ASN Type, ASN ISO, MCC, MNC

This API works without an API key, but there are limitations. Up to 50 queries per day are free. If you need more queries, you can sign up for a free plan and get an API key in just a few clicks!


# Rate Limiting

Our free tier provides 10,000 API access requests per month, after which the free tier is no longer available.

Paid tiers include monthly request limits and configurable alerts to help you stay on top of things. If you exceed your tier limit, you will receive a 429 HTTP status code - but your usage will not be immediately stopped, you will be charged for metered usage, and you can automatically extend your usage time without interruption.


# Request method and parameters

The REST API calling syntax. Please see the table below for the description of each parameter.

API Address

```
https://api.ipnews.io/query/query_ip?key={YOUR_API_KEY}&ip=8.8.8.8      
```

| Request        | Output | Character |
| -------------- | ------ | --------- |
| HTTPS GET/POST | Json   | UTF-8     |

Parameters

| Name | Type   | Description                                                             |
| ---- | ------ | ----------------------------------------------------------------------- |
| ip   | String | Status code of the request rSupports both IPV4 and IPV6 queries         |
| key  | String | Use the secret key and permission verification of the purchased package |


# Response Format

The REST API returns the following fields and values.

| Name    | Type       | Description                                                                     |
| ------- | ---------- | ------------------------------------------------------------------------------- |
| code    | String     | Query result status code                                                        |
| message | String     | If the request is successful, the content is success                            |
| data    | JsonObject | The returned data field. If the request fails, please refer to the status code. |

#### data Response

<table><thead><tr><th width="253">Name</th><th>Description</th></tr></thead><tbody><tr><td>country</td><td>Country name.</td></tr><tr><td>country_code</td><td>Two-character country code based on ISO 3166.</td></tr><tr><td>region</td><td>Region or state name.</td></tr><tr><td>city</td><td>City name.</td></tr><tr><td>latitude</td><td>City latitude. Defaults to capital city latitude if city is unknown.</td></tr><tr><td>longitude</td><td>City longitude. Defaults to capital city longitude if city is unknown.</td></tr><tr><td>isp</td><td>Internet Service Provider or company's name.</td></tr><tr><td>time_zone</td><td>Current region's time zone.</td></tr><tr><td>area_code</td><td>A varying length number assigned to geographic areas for calls between cities.</td></tr><tr><td>postal</td><td>ZIP/Postal code.</td></tr><tr><td>weather_station_code</td><td>The special code to identify the nearest weather observation station.</td></tr><tr><td>elevation</td><td>Average height of city above sea level in meters (m).</td></tr><tr><td>currency</td><td>Currency of the country.</td></tr><tr><td>eu_member</td><td>Whether it belongs to the European Union.</td></tr><tr><td>country_capital</td><td>The name of the current country's capital.</td></tr><tr><td>radius</td><td>The current query positioning radius.</td></tr><tr><td>is_proxy</td><td>Whether it is a proxy IP.</td></tr><tr><td>risk_label</td><td>Possible risky behavior labels.</td></tr><tr><td>risk_score</td><td>The security score of the IP address, the higher the score, the more dangerous it is.</td></tr><tr><td>risk_level</td><td>The risk level is defined based on the risk score.</td></tr><tr><td>ip_usage_type</td><td>IP address usage types, including:<br>Data center（IDC）<br>Home broadband (ISP)<br>Commercial(GTW)<br>Government(GOV)<br>Education(EDU)<br>Content distribution(CDN)<br>Mobile network(MOB)<br>Reserved(BOGON)</td></tr><tr><td>asn</td><td>Autonomous system number (ASN).</td></tr><tr><td>asn_domain</td><td>The domain name associated with the Autonomous System Number (ASN).</td></tr><tr><td>asn_organization</td><td>Autonomous System Number (ASN) Owner.</td></tr><tr><td>asn_type</td><td>Autonomous System Number (ASN) usage types:<br>Home broadband (ISP)<br>Hosting（IDC）<br>Education(EDU)<br>Government(GOV)<br>Commercial(GTW)</td></tr><tr><td>asn_country</td><td>Autonomous System Number (ASN) Country.</td></tr><tr><td>carrier_mcc</td><td>Mobile Country Codes (MCC) as defined in ITU E.212 for use in identifying mobile stations in wireless telephone networks, particularly GSM and UMTS networks.</td></tr><tr><td>carrier_mnc</td><td>Mobile Network Code (MNC) is used in combination with a Mobile Country Code (MCC) to uniquely identify a mobile phone operator or carrier.</td></tr><tr><td>carrier_name</td><td>The name of the mobile operator.</td></tr><tr><td>domains_total</td><td>The number of domain names associated with the IP.</td></tr><tr><td>domains_name</td><td>The domain name associated with the IP.</td></tr></tbody></table>

```
Code example
```

```
{
    "code": 200,
    "data": {
        "ip_info": {
            "ip": "63.105.0.0",
            "version": "V4",
            "country_code": "US",
            "country": "United States",
            "region": "New York",
            "city": "New York City",
            "isp": "Verizon Business",
            "longitude": "-74.005970",
            "latitude": "40.714270",
            "time_zone": "America/New_York",
            "area_code": "+1",
            "postal": "10001",
            "weather_station_code": "usw00094728",
            "elevation": "﹢10",
            "currency": "US",
            "eu_member": "true",
            "country_capital": "Washington, D.C",
            "radius": "7",
            "asn": "AS701",
            "asn_domain": "verizon.com",
            "asn_organization": "Verizon Business",
            "asn_type": "DYN",
            "asn_country": "US",
            "risk_level": "low",
            "is_proxy": "false",
            "risk_score": "0",
            "risk_label": "-",
            "ip_usage_type": "Fixed Line ISP",
            "carrier_mcc": "310",
            "carrier_mnc": "004",
            "carrier_name": "Verizon Business",
            "domains_total": "",
            "domains_name": ""
        },
        "search_count": 0
    },
    "msg": "success"
}
```

<br>


# Query result status code

Each time a query succeeds or fails, a code object is returned.

| Code | Description                                                                                 |
| ---- | ------------------------------------------------------------------------------------------- |
| 200  | Query successful                                                                            |
| 400  | Error in querying IP address                                                                |
| 401  | Access key error                                                                            |
| 402  | Your package has been used up. Please settle the excess charges or renew your subscription. |
| 408  | Network delay, request processing time is too long                                          |
| 500  | An unknown internal error occurred.                                                         |
| 501  | The access IP address is not included in the whitelist.                                     |


# Getting Started

You can download your subscribed database in two different ways:

* Database download from your dashboard on the webpage
* Database download from using URL on your terminal

**Option 1: Database download from the account dashboard**

You can visit the Data Downloads page to download your database.

The steps are as follows:

1. Access your account information
2. Select the database file type to download, including: DAT, TXT, CSV file formats.
3. Download the corresponding database.

#### Option 2: Download the database using a URL

You can use the data download URL to download your database.

1. Access your account information
2. Select the database file type to download, including: DAT, TXT, CSV file formats.
3. Click the Copy button to copy the download URL of the corresponding database


# Database Type

IP NEWS provides various types of databases, covering comprehensive IP address data dimensions.

### The free databases include the following types:

{% content-ref url="/pages/YUZSyZ5Nbm7Y18sK4D7r" %}
[Free IP to Country](/database-documentation/markdown/free-ip-to-country)
{% endcontent-ref %}

{% content-ref url="/pages/mSeW7qrpC5Kkjt6SQrUF" %}
[Free IP to ASN](/database-documentation/markdown/free-ip-to-asn)
{% endcontent-ref %}

{% content-ref url="/pages/mw7mBdHjU0XkTjD4K2T4" %}
[Free IP to Country+IP to ASN](/database-documentation/markdown/free-ip-to-country+ip-to-asn)
{% endcontent-ref %}

### The paid databases include the following types:

{% content-ref url="/pages/LpaKmwdUNUBaKpjSgOh7" %}
[IP Geolocation Database](/database-documentation/markdown/ip-geolocation-database)
{% endcontent-ref %}

{% content-ref url="/pages/xaHVrPj0cPsyc6EiiowF" %}
[IP Risk Database](/database-documentation/markdown/ip-risk-database)
{% endcontent-ref %}

{% content-ref url="/pages/X1CQbUTzY8O7ysK3HWcV" %}
[ASN Database](/database-documentation/markdown/asn-database)
{% endcontent-ref %}

{% content-ref url="/pages/OcAm3eP317Qmv58x80fc" %}
[Usage Type Database](/database-documentation/markdown/usage-type-database)
{% endcontent-ref %}

{% content-ref url="/pages/4lbK18ID9wnuSs4XNEjm" %}
[Mobile Carrier Database](/database-documentation/markdown/mobile-carrier-database)
{% endcontent-ref %}

{% content-ref url="/pages/xbEsMJyyMeJ2WSw2EfDZ" %}
[Hosted Domains Database](/database-documentation/markdown/hosted-domains-database)
{% endcontent-ref %}


# Free IP to Country

The Free IP to Geolocation Database provides geolocation information identified from an IP address, providing only: Country, Region/State, Country, Latitude and Longitude data.

#### Database Schema

The database contains the following fields:

| Field Name    | Data Type | Description                             |
| ------------- | --------- | --------------------------------------- |
| country       | TEXT      | Country of IP address                   |
| country\_code | TEXT      | ISO 3166 country code of the ip address |
| region        | TEXT      | Region/State of the IP address          |
| latitude      | TEXT      | Longitude value of the IP address       |
| longitude     | TEXT      | Latitude value of the IP address        |

#### Sample Database

* Free IP to Country-CSV
* Free IP to Country-DAT
* Free IP to Country-TXT


# Free IP to ASN

The free IP to ASN database provides geographic location information identified by IP address, only providing: ASN number, ASN organization.

#### Database Schema

The database contains the following fields:

| Field Name        | Data Type | Description                    |
| ----------------- | --------- | ------------------------------ |
| asn               | TEXT      | Autonomous System Number (ASN) |
| asn\_organization | TEXT      | Name of the ASN organization   |

#### Sample Database

* Free IP to ASN-CSV
  * Free IP to ASN-DAT
* Free IP to ASN-TXT


# Free IP to Country+IP to ASN

The free IP database provides geographic location information identified by IP address, and only provides: Country, Region/State, Country, Latitude and Longitude data, ASN number, ASN organization.

#### Database Schema

The database contains the following fields:

| Field Name        | Data Type | Description                             |
| ----------------- | --------- | --------------------------------------- |
| country           | TEXT      | Country of IP address                   |
| country\_code     | TEXT      | ISO 3166 country code of the ip address |
| region            | TEXT      | Region/State of the IP address          |
| latitude          | FLOAT     | Longitude value of the IP address       |
| longitude         | FLOAT     | Latitude value of the IP address        |
| asn               | TEXT      | Autonomous System Number (ASN)          |
| asn\_organization | TEXT      |                                         |


# IP Geolocation Database

IPnews-IP geolocation database provides you with accurate data insights to meet your various business needs. Data services provided to product architects, developers, etc., including IP geolocation data such as countries, cities, regions, latitude and longitude, time zones, operators, etc.

#### Database Schema

The database contains the following fields:

<table><thead><tr><th width="253">Name</th><th>Data Type</th><th>Description</th></tr></thead><tbody><tr><td>country</td><td>TEXT</td><td>Country name.</td></tr><tr><td>country_code</td><td>TEXT</td><td>Two-character country code based on ISO 3166.</td></tr><tr><td>region</td><td>TEXT</td><td>Region or state name.</td></tr><tr><td>city</td><td>TEXT</td><td>City name.</td></tr><tr><td>latitude</td><td>FLOAT</td><td>City latitude. Defaults to capital city latitude if city is unknown.</td></tr><tr><td>longitude</td><td>FLOAT</td><td>City longitude. Defaults to capital city longitude if city is unknown.</td></tr><tr><td>isp</td><td>TEXT</td><td>Internet Service Provider or company's name.</td></tr><tr><td>time_zone</td><td>TEXT</td><td>Current region's time zone.</td></tr><tr><td>area_code</td><td>TEXT</td><td>A varying length number assigned to geographic areas for calls between cities.</td></tr><tr><td>postal</td><td>TEXT</td><td>ZIP/Postal code.</td></tr><tr><td>weather_station_code</td><td>TEXT</td><td>The special code to identify the nearest weather observation station.</td></tr><tr><td>elevation</td><td>TEXT</td><td>Average height of city above sea level in meters (m).</td></tr><tr><td>currency</td><td>TEXT</td><td>Currency of the country.</td></tr><tr><td>eu_member</td><td>BOOLEAN</td><td>Whether it belongs to the European Union.</td></tr><tr><td>country_capital</td><td>TEXT</td><td>The name of the current country's capital.</td></tr><tr><td>radius</td><td>TEXT</td><td>The current query positioning radius.</td></tr></tbody></table>


# IP Risk Database

IPnews-IP Risk Database allows users to use our accurate and real-time updated IP data to identify relevant IPs of VPN, proxy, Tor node, transit and hosting providers. With this data, you can enhance threat intelligence, reduce the risk of advertising and gaming fraud, protect digital content security, and optimize fraud detection models to make smarter and safer decisions.

#### Database Schema

The database contains the following fields:

|             |         |                                                                                       |
| ----------- | ------- | ------------------------------------------------------------------------------------- |
| is\_proxy   | BOOLEAN | Whether it is a proxy IP.                                                             |
| risk\_label | TEXT    | Possible risky behavior labels.                                                       |
| risk\_score | TEXT    | The security score of the IP address, the higher the score, the more dangerous it is. |
| risk\_level | TEXT    | The risk level is defined based on the risk score.                                    |


# ASN Database

Autonomous System Number (ASN) is a key element for the stable operation of the Internet. It represents a collection of IP networks managed by network operators and is extremely important for network analysis and management. IPnews' ASN database download service is designed for IP and network analysis and provides comprehensive data resources.

#### Database Schema

The database contains the following fields:

| Name               | Data Type | Description                                                         |
| ------------------ | --------- | ------------------------------------------------------------------- |
| asn                | TEXT      | Autonomous system number (ASN).                                     |
| asn\_domain        | TEXT      | The domain name associated with the Autonomous System Number (ASN). |
| asn\_organization  | TEXT      | Autonomous System Number (ASN) Owner.                               |
| asn\_type          | TEXT      | <p>Autonomous System Number (ASN) usage types:                      |
| <br>ISP            |           |                                                                     |
| <br>Hosting        |           |                                                                     |
| <br>Education      |           |                                                                     |
| <br>Government     |           |                                                                     |
| <br>Commercial</p> |           |                                                                     |
| asn\_country       | TEXT      | Autonomous System Number (ASN) Country.                             |


# Usage Type Database

IPnews-Usage Type Database products provide comprehensive IP address analysis and application solutions for multiple fields, identifying more than 20 application scenarios of IP addresses such as enterprise dedicated lines, home broadband, and data centers, helping users gain an in-depth understanding of IP sources, uses, and risks.

#### Database Schema

The database contains the following fields:

| Name                     | Data Type | Description                           |
| ------------------------ | --------- | ------------------------------------- |
| ip\_usage\_type          | TEXT      | <p>IP address usage types, including: |
| <br>Data center          |           |                                       |
| <br>Home broadband (ISP) |           |                                       |
| <br>Commercial           |           |                                       |
| <br>Government           |           |                                       |
| <br>Education            |           |                                       |
| <br>Content distribution |           |                                       |
| <br>Mobile network       |           |                                       |
| <br>Reserved</p>         |           |                                       |


# Mobile Carrier Database

IP-Mobile Operator Repository Download Integration provides access to billions of mobile operator data. Help enterprises optimize website appearance through user insights, and target advertising and discount strategies based on mobile users.

#### Database Schema

The database contains the following fields:

| Name          | Data Type | Description                                                                                                                                                   |
| ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| carrier\_mcc  | TEXT      | Mobile Country Codes (MCC) as defined in ITU E.212 for use in identifying mobile stations in wireless telephone networks, particularly GSM and UMTS networks. |
| carrier\_mnc  | TEXT      | Mobile Network Code (MNC) is used in combination with a Mobile Country Code (MCC) to uniquely identify a mobile phone operator or carrier.                    |
| carrier\_name | TEXT      | The name of the mobile operator.                                                                                                                              |


# Hosted Domains Database

IPnews-IP hosting dataset insights, you can instantly access billions of hosted domain data to help users enrich lead generation data; identify websites on the same hosting server that endanger infrastructure security, customize website appearance, etc.

#### Database Schema

The database contains the following fields:

| Name           | Data Type | Description                                        |
| -------------- | --------- | -------------------------------------------------- |
| domains\_total | TEXT      | The number of domain names associated with the IP. |
| domains\_name  | TEXT      | The domain name associated with the IP.            |


# Offline library parsing code

Provide IPv4/IPv6 database access code, you can access it according to the required development language


# IPv4


# DAT format access code

IPv4 access code：

{% tabs %}
{% tab title="JavaScript" %}

```javascript
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

public class Ip {

    private int[] prefStart = new int[256];
    private int[] prefEnd = new int[256];
    private byte[] data;
    private static Ip instance = null;

    private Ip(String fileName) {
        Path path = Paths.get(fileName);

        try {
            data = Files.readAllBytes(path);
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (int k = 0; k < 256; k++) {
            int i = k * 8 + 4;
            prefStart[k] = (int) unpackInt4byte(data[i], data[i + 1], data[i + 2], data[i + 3]);
            prefEnd[k] = (int) unpackInt4byte(data[i + 4], data[i + 5], data[i + 6], data[i + 7]);
        }
    }

    public static synchronized Ip getInstance(String fileName) {
        if (instance == null)
            instance = new Ip(fileName);
        return instance;
    }

    public String get(String ip) {

        String[] ips = ip.split("\\.");
        int pref = Integer.valueOf(ips[0]);
        long val = ipToLong(ip);
        int low = prefStart[pref], high = prefEnd[pref];
        long cur = low == high ? low : search(low, high, val);
        return getAddr((int) cur);

    }

    private String getAddr(int cur){
        int p = 2052 + (cur * 9);
        int offset = (int)unpackInt4byte(data[4 + p], data[5 + p], data[6 + p],data[7+p]);
        int length = data[8 + p] & 0xff;
        return new String(Arrays.copyOfRange(data,offset,offset + length));
    }

    private int search(int low, int high, long k) {
        int M = 0;
        while (low <= high) {
            int mid = (low + high) / 2;

            int p = 2052 + (mid * 9);
            long endipNum = unpackInt4byte(data[p], data[1 + p], data[2 + p], data[3 + p]);

            if (endipNum >= k) {
                M = mid;
                if (mid == 0) {
                    break;
                }
                high = mid - 1;
            } else
                low = mid + 1;
        }
        return M;
    }

    private long unpackInt4byte(byte a, byte b, byte c, byte d) {
        return (a & 0xFFL) | ((b << 8) & 0xFF00L) | ((c << 16) & 0xFF0000L) | ((d << 24) & 0xFF000000L);

    }

    private long ipToLong(String ip) {
        long result = 0;
        String[] d = ip.split("\\.");
        for (String b : d) {
            result <<= 8;
            result |= Long.parseLong(b) & 0xff;
        }
        return result;
    }


}


```

{% endtab %}

{% tab title="Python" %}

```python
import mmap
import struct
import socket


class IPV4Find:
    def __init__(self, file_name):

        self.buchang = 9
        self._handle = open(file_name, "rb")
        self.data = mmap.mmap(self._handle.fileno(), 0, access=mmap.ACCESS_READ)
        self.prefArr = []
        record_size = self.unpack_int_4byte(0)
        i = 0
        while i < 256:
            p = i * 8 + 4
            self.prefArr.append([self.unpack_int_4byte(p), self.unpack_int_4byte(p + 4)])
            i += 1
        self.endArr = []

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        self.close()

    def close(self):
        self._handle.close()

    def get(self, ip):

        ipdot = ip.split('.')
        prefix = int(ipdot[0])
        if prefix < 0 or prefix > 255 or len(ipdot) != 4:
            raise ValueError("invalid ip address")
        intIP = self.ip_to_int(ip)
        low = self.prefArr[prefix][0]
        high = self.prefArr[prefix][1]
        cur = low if low == high else self.search(low, high, intIP)
        # return self.addrArr[cur]
        return self.get_addr(cur)

    def search(self, low, high, k):
        M = 0
        while low <= high:
            mid = (low + high) // 2
            end_ip_num = self.unpack_int_4byte(2052 + (mid * self.buchang))
            if end_ip_num >= k:
                M = mid
                if mid == 0:
                    break
                high = mid - 1
            else:
                low = mid + 1
        return M

    def ip_to_int(self, ip):
        _ip = socket.inet_aton(ip)
        return struct.unpack("!L", _ip)[0]

    def unpack_int_4byte(self, offset):
        return struct.unpack('<L', self.data[offset:offset + 4])[0]

    def unpack_int_1byte(self, offset):
        return struct.unpack('B', self.data[offset:offset + 1])[0]

    def unpack_int_8byte(self, offset):
        return struct.unpack('<Q', self.data[offset:offset + 8])[0]

    def unpack_int_2byte(self, offset):
        return struct.unpack('<H', self.data[offset:offset + 2])[0]

    def get_addr(self, j):
        p = 2052 + (j * self.buchang)

        offset = self.unpack_int_4byte(4 + p)
        length = self.unpack_int_1byte(8 + p)
        return self.data[offset:offset + length].decode('utf-8')

```

{% endtab %}

{% tab title="C#" %}

```csharp
using System.Net;
using System.Text;

namespace ipnews_dat
{

    public class IPSearch
    {
        private static readonly Lazy<IPSearch> lazy = new Lazy<IPSearch>(() => new IPSearch());
        public static IPSearch Instance { get { return lazy.Value; } }
        private IPSearch()
        {
            LoadDat();
        }

        private string datPath = "D:\\databases\\ipnews_scenes_mob.dat";

        private DateTime lastRead = DateTime.MinValue;

        private long[,] prefmap = new long[256, 2];

        private string[] addrArr;
        private byte[] data;



        /// <summary>
        /// initialize binary  ipnews.dat 
        /// </summary>

        private void LoadDat()
        {
            data = File.ReadAllBytes(datPath);

            for (int k = 0; k < 256; k++)
            {
                int i = k * 8 + 4;
                int prefix = k;
                long startIndex = ReadLittleEndian32(data[i], data[i + 1], data[i + 2], data[i + 3]);
                long endIndex = ReadLittleEndian32(data[i + 4], data[i + 5], data[i + 6], data[i + 7]);
                prefmap[k, 0] = startIndex; prefmap[k, 1] = endIndex;
            }


        }



        public string Find(string ip)
        {
            String[] ips = ip.Split(".");
            long pref = long.Parse(ips[0]);
            long val = IpToLong(ip);
            long low = prefmap[pref, 0], high = prefmap[pref, 1];
            long cur = low == high ? low : BinarySearch(low, high, val);

            return cur > -1 ? GetAddr(cur) : "||||||||||||||||||";
        }


        public string GetAddr(long cur)
        {
            long p = 2052 + (cur * 9);
            int offset = data[4 + p] + ((data[5 + p]) << 8) + ((data[6 + p]) << 16) + ((data[7 + p]) << 24);
            int length = data[8 + p];
            return Encoding.UTF8.GetString(data, offset, length);
        }


        
        private long BinarySearch(long low, long high, long k)
        {
            long M = 0, mid = 0;
            while (low <= high)
            {
                mid = (low + high) / 2;

                long p = 2052 + (mid * 9);
                uint endipnum = ReadLittleEndian32(data[p], data[1 + p], data[2 + p], data[3 + p]);

                if (endipnum >= k)
                {
                    M = mid;
                    if (mid == 0)
                    {
                        break;
                    }
                    high = mid - 1;
                }
                else
                    low = mid + 1;
            }
            return M;
        }


        static long IpToLong(string ipAddress)
        {
            IPAddress ip = IPAddress.Parse(ipAddress);

            byte[] bytes = ip.GetAddressBytes();

            Array.Reverse(bytes); 
            return BitConverter.ToUInt32(bytes, 0);
        }



        private uint ReadBigEndian32(byte a, byte b, byte c, byte d)
        {
            return (uint)((a << 24) | (b << 16) | (c << 8) | d);
        }


        private uint ReadLittleEndian32(byte a, byte b, byte c, byte d)
        {
            return (uint)((a | (b << 8) | (c << 16) | (d << 24)));
        }

      
    }


}
```

{% endtab %}

{% tab title="C++" %}

#### ipnews.cpp

```cpp
#include "ipnews.h"
#include <iostream>
#include "string.h"
#include <stdlib.h>

//char* IP_FILENAME = "F:\\ipv4_city.dat";


geo_ip *ip_instance(const char *fileName) {
    static geo_ip *instance = NULL;
    if (instance == NULL) {
        instance = new geo_ip();
        if (ip_loadDat(instance, fileName) >= 0) {
            return instance;
        }
        if (instance == NULL) {
            delete instance;
        }
        return NULL;
    }
    return instance;
}

int32_t ip_loadDat(geo_ip *p, const char *fileName) {
    FILE *file;

    long len = 0;
    int k, i, j;
    file = fopen(fileName, "rb");
    if (file == NULL) {
        printf("%s", "Open file error");
        return -2;
    }
    fseek(file, 0, SEEK_END);
    len = ftell(file);
    fseek(file, 0, SEEK_SET);
    p->buffer = (uint8_t *) malloc(len * sizeof(uint8_t));
    fread(p->buffer, 1, len, file);
    fclose(file);

    for (k = 0; k < 256; k++) {
        i = k * 8 + 4;
        p->prefStart[k] = ip_read_int32(p, i);
        p->prefEnd[k] = ip_read_int32(p, i + 4);
    }

    return 0;
}

char *ip_query(geo_ip *p, const char *ip) {
    uint32_t pref, cur, intIP, low, high;
    if (NULL == p) {
        return NULL;
    }
    intIP = ip_ip2long(p, ip, &pref);;
    low = p->prefStart[pref];
    high = p->prefEnd[pref];
    cur = (low == high) ? low : ip_binary_search(p, low, high, intIP);
    if (cur == 100000000) {
        char *nil = (char *) malloc(2 * sizeof(char));
        nil[0] = '|';
        nil[1] = '\0';
        return nil;
    }
    return get_addr(p, cur);
}

long ip_binary_search(geo_ip *p, long low, long high, long k) {
    long M = 0;
    while (low <= high) {
        long mid = (low + high) / 2;
        int j = 2052 + (mid * 9);
        uint32_t endipNum = ip_read_int32(p,  j);
        if (endipNum >= k) {
            M = mid;
            if (mid == 0) {
                break;
            }
            high = mid - 1;
        } else
            low = mid + 1;
    }
    return M;
}

char *get_addr(geo_ip *p, uint32_t j) {
    uint32_t index = 2052 + j * 9;
    uint32_t offset = ip_read_int32(p,  index + 4);
    uint32_t length = p->buffer[index + 8];
    try {
        char *result = (char *) malloc((length + 1) * sizeof(char));
        memcpy(result, p->buffer + offset, length);
        result[length] = '\0';
        return result;
    }catch (...) {
        char* result = (char *) malloc((18 + 1) * sizeof(char));
        strcpy(result, "||||||||||||||||||");
        return result;
    }

}

uint32_t ip_ip2long(geo_ip *p, const char *addr, uint32_t *prefix) {
    uint32_t c, octet, t;
    uint32_t ipnum;
    int i = 3;

    octet = ipnum = 0;
    while ((c = *addr++)) {
        if (c == '.') {
            ipnum <<= 8;
            ipnum += octet;
            i--;
            octet = 0;
        } else {
            t = octet;
            octet <<= 3;
            octet += t;
            octet += t;
            c -= '0';

            octet += c;
            if (i == 3) {
                *prefix = octet;
            }
        }
    }

    ipnum <<= 8;

    return ipnum + octet;
}

uint32_t ip_read_int32(geo_ip *p, int pos) {
    uint32_t result;
    result = (uint32_t) ((p->buffer[pos]) | (p->buffer[pos + 1] << 8) | (p->buffer[pos + 2] << 16) | (p->buffer[pos + 3] << 24));
    return result;
}

```

#### ipnews.h

```
#ifndef __IP_H4_
#define __IP_H4_

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct tag_ip {
    uint32_t prefStart[256];
    uint32_t prefEnd[256];
    uint8_t *buffer;
} geo_ip;

geo_ip *ip_instance(const char *fileName);

int32_t ip_loadDat(geo_ip *p, const char *fileName);

char *ip_query(geo_ip *p, const char *ip);

long ip_binary_search(geo_ip *p, long low, long high, long k);

uint32_t ip_ip2long(geo_ip *p, const char *addr, uint32_t *prefix);

uint32_t ip_read_int32(geo_ip *p, int pos);


char *get_addr(geo_ip *p, uint32_t j);
;
#ifdef __cplusplus
}
#endif
#endif
```

#### main.cpp

```cpp
#include<iostream>
#include<memory>
#include "ipDataCloud.h"


int main()
{
    std::unique_ptr<geo_ip> finder(ip_instance("file_path"));
    const char* ip = "ip_address";
    char* result = ip_query(finder.get(), ip);
    std::cout << result << std::endl;
    free(result);
    result = NULL;
	
}
```

{% endtab %}

{% tab title="Go" %}

```go
package ipnews

import (
	"encoding/binary"
	"errors"
	"io/ioutil"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
)

type IpInfo struct {
	prefStart [256]uint32
	prefEnd   [256]uint32
	data      []byte
}

var obj *IpInfo
var once sync.Once

func GetObject(fileName string) *IpInfo {
	once.Do(func() {
		obj = &IpInfo{}
		var err error
		obj, err = LoadFile(fileName)
		if err != nil {
			log.Fatal("the IP Dat loaded failed!")
		}
	})
	return obj
}

func LoadFile(file string) (*IpInfo, error) {
	p := IpInfo{}
	data, err := ioutil.ReadFile(file)
	p.data = data
	if err != nil {
		return nil, err
	}

	for k := 0; k < 256; k++ {
		i := k*8 + 4
		p.prefStart[k] = unpackInt4byte(data[i], data[i+1], data[i+2], data[i+3])
		p.prefEnd[k] = unpackInt4byte(data[i+4], data[i+5], data[i+6], data[i+7])
	}

	return &p, err

}

func (p *IpInfo) Get(ip string) (string, error) {
	ips := strings.Split(ip, ".")
	x, _ := strconv.Atoi(ips[0])
	prefix := uint32(x)
	intIP, err := ipToInt(ip)
	if err != nil {
		return "", err
	}

	low := p.prefStart[prefix]
	high := p.prefEnd[prefix]

	var cur uint32
	if low == high {
		cur = low
	} else {
		cur = p.Search(low, high, intIP)
	}
	if cur == 100000000 {
		return "无信息", errors.New("无信息")
	} else {
		return p.getAddr(cur), nil
	}

}

func (p *IpInfo) Search(low uint32, high uint32, k uint32) uint32 {
	var M uint32 = 0
	for low <= high {
		mid := (low + high) / 2

		j := 2052 + (mid * 9)
		endipNum := unpackInt4byte(p.data[j], p.data[1+j], p.data[2+j], p.data[3+j])

		if endipNum >= k {
			M = mid
			if mid == 0 {
				break
			}
			high = mid - 1
		} else {
			low = mid + 1
		}
	}

	return M
}

func (p *IpInfo) getAddr(cur uint32) string {
	j := 2052 + (cur * 9)
	offset := unpackInt4byte(p.data[4+j], p.data[5+j], p.data[6+j], p.data[7+j])
	length := uint32(p.data[8+j])
	return string(p.data[offset:int(offset+length)])
}

func ipToInt(ipstr string) (uint32, error) {
	ip := net.ParseIP(ipstr)
	ip = ip.To4()
	if ip == nil {
		return 0, errors.New("ip 不合法")
	}
	return binary.BigEndian.Uint32(ip), nil
}

func unpackInt4byte(a, b, c, d byte) uint32 {
	return (uint32(a) & 0xFF) | ((uint32(b) << 8) & 0xFF00) | ((uint32(c) << 16) & 0xFF0000) | ((uint32(d) << 24) & 0xFF000000)
}


```

{% endtab %}

{% tab title="Lua" %}

#### **ipdata.lua**

```lua
local ipdata = { prefStart = {}, prefEnd = {},
                 endArr = {}, addrArr = {},
                 file = {}, _version = "0.1.2" }

function unpackInt4byte(a, b, c, d)
    return (a & 0xFF) | ((b << 8) & 0xFF00) | ((c << 16) & 0xFF0000) | ((d << 24) & 0xFF000000)
end

function fsize(file)
    local current = file:seek() 
    local size = file:seek("end") 
    file:seek("set", current) 
    return size
end

function redaFileBytes(file, length, off)
    file:seek("set", off)
    local str = file:read(length)
    return { str:byte(1, #str) }
end

function ipdata.LoadFile(path)
    local sizeData = {}

    ipdata.file = io.open(path, "rb")

    for k = 1, 256 do
        local i = k * 8 + 4
        sizeData = redaFileBytes(ipdata.file, 8, i)
        ipdata.prefStart[k] = unpackInt4byte(sizeData[1], sizeData[2], sizeData[3], sizeData[4])
        ipdata.prefEnd[k] = unpackInt4byte(sizeData[5], sizeData[6], sizeData[7], sizeData[8])
    end
    -- 文件大小
    sizeData = redaFileBytes(ipdata.file, 4, 0)
    local recordSize = unpackInt4byte(sizeData[1], sizeData[2], sizeData[3], sizeData[4])
    
    for i = 1, recordSize do
        local j = 2052 + (i * 9)
        sizeData = redaFileBytes(ipdata.file, 9, j)
        local endipnum = unpackInt4byte(sizeData[1], sizeData[2], sizeData[3], sizeData[4])
        ipdata.endArr[i] = endipnum
       
    end
end

function ipdata.getAddr(row)
    local j = 2052 + (row * 9)
    ipdata.file:seek("set", j + 4)
    local tempIndexData = ipdata.file:read(5)
    local offset = unpackInt4byte(tempIndexData:byte(1, 4))
    local length = tempIndexData:byte(5)
    ipdata.file:seek("set", offset)
    local tempData = ipdata.file:read(length)
    return tempData
end


function ip2int(ip)
    local o1, o2, o3, o4 = ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
    local num = 2 ^ 24 * o1 + 2 ^ 16 * o2 + 2 ^ 8 * o3 + o4
    return math.floor(num)
end

function ipdata.Search(low, high, k)
    local M = 0
    while low <= high
    do
        local mid = math.floor((low + high) / 2)
        local endipNum = ipdata.endArr[mid]
        if endipNum == nil then
            break
        end
        if endipNum >= k then
            M = mid
            if mid == 0 then
                break
            end
            high = mid - 1
        else
            low = mid + 1
        end
    end
    return M
end

function ipdata.FindIP(ip)
    local ips = { string.match(ip, "([^.]+).?") }
    local prefix = tonumber(ips[1])

    local low = ipdata.prefStart[prefix]
    local high = ipdata.prefEnd[prefix]
    local intIP = ip2int(ip)

    -- print("FindIP", ip, prefix, intIP)
    local cur = 0
    if low == high then
        cur = low
    else
        cur = ipdata.Search(low, high, intIP)
    end
    if cur == 100000000 then
        return "0.0.0.0"
    else
        -- return ipdata.addrArr[cur]
        return ipdata.getAddr(cur)
    end
end

return ipdata
```

#### **ipdata\_5.1.4.lua**

```lua
ipdata = { prefStart = {}, prefEnd = {},
    endArr = {}, addrArr = {},
    file = {}, _version = "0.1.2" }

function unpackInt4byte(a, b, c, d)
    return (a % 256) + (b * 256) + (c * 65536) + (d * 16777216)
end

function fsize(file)
    local current = file:seek()
    local size = file:seek("end")
    file:seek("set", current)
    return size
end

function redaFileBytes(file, length, off)
    file:seek("set", off)
    local str = file:read(length)
    local bytes = {}
    for i = 1, #str do
        table.insert(bytes, string.byte(str, i))
    end
    return bytes
end

function ipdata.LoadFile(path)
    local sizeData = {}

    ipdata.file = io.open(path, "rb")

    for k = 1, 256 do
        local i = k * 8 + 4
        sizeData = redaFileBytes(ipdata.file, 8, i)
        ipdata.prefStart[k] = unpackInt4byte(sizeData[1], sizeData[2], sizeData[3], sizeData[4])
        ipdata.prefEnd[k] = unpackInt4byte(sizeData[5], sizeData[6], sizeData[7], sizeData[8])
    end

    sizeData = redaFileBytes(ipdata.file, 4, 0)
    local recordSize = unpackInt4byte(sizeData[1], sizeData[2], sizeData[3], sizeData[4])

    local allsizeData = redaFileBytes(ipdata.file, recordSize*9+9, 2052)
    for i = 1, recordSize do
        local x = i * 9
		local a = 1 + x
		local b = 2 + x
		local c = 3 + x
		local d = 4 + x
		local endipnum2 = unpackInt4byte(allsizeData[a], allsizeData[b], allsizeData[c], allsizeData[d])
		ipdata.endArr[i] = endipnum2

    end
end

function ipdata.getAddr(row)
    local j = 2052 + (row * 9)
    ipdata.file:seek("set", j + 4)
    local tempIndexData = ipdata.file:read(5)
    local offset = unpackInt4byte(string.byte(tempIndexData, 1), string.byte(tempIndexData, 2), string.byte(tempIndexData, 3), string.byte(tempIndexData, 4))
    local length = string.byte(tempIndexData, 5)
    ipdata.file:seek("set", offset)
    local tempData = ipdata.file:read(length)
    return tempData
end

function ip2int(ip)
    local o1, o2, o3, o4 = string.match(ip, "(%d+)%.(%d+)%.(%d+)%.(%d+)")
    local num = 2 ^ 24 * o1 + 2 ^ 16 * o2 + 2 ^ 8 * o3 + o4
    return math.floor(num)
end

function ipdata.Search(low, high, k)
    local M = 0
    while low <= high do
        local mid = math.floor((low + high) / 2)
        local endipNum = ipdata.endArr[mid]
        if endipNum == nil then
            break
        end
        if endipNum >= k then
            M = mid
            if mid == 0 then
                break
            end
            high = mid - 1
        else
            low = mid + 1
        end
    end
    return M
end

function ipdata.FindIP(ip)
    local ips = { string.match(ip, "([^.]+).?") }
    local prefix = tonumber(ips[1])

    local low = ipdata.prefStart[prefix]
    local high = ipdata.prefEnd[prefix]
    local intIP = ip2int(ip)

    local cur = 0
    if low == high then
        cur = low
    else
        cur = ipdata.Search(low, high, intIP)
    end

    if cur == 100000000 then
        return "0.0.0.0"
    else
        return ipdata.getAddr(cur)
    end
end

return ipdata
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
/* 
PHP Version 7+
*/
class Ip
{

    private $prefStart = array();
    private $prefEnd = array();
    private $endArr = array();
    private $data;

    static private $instance = null;

    private function __construct()
    {
        $this->loadFile();
    }

    private function loadFile()
    {
        $path = 'ipv4.dat';
        $fp = fopen($path, 'rb');
        $fsize = filesize($path);

        $this->data = fread($fp, $fsize);

        for ($k = 0; $k < 256; $k++) {
            $i = $k * 8 + 4;
            $this->prefStart[$k] = unpack("V", $this->data, $i)[1];
            $this->prefEnd[$k] = unpack("V", $this->data, $i + 4)[1];
        }
        fclose($fp);
    }

    

    private function __clone()
    {
    }

    private function __wakeup()
    {
    }

    public static function getInstance()
    {
        if (self::$instance instanceof Ip) {
            return self::$instance;
        } else {

            return self::$instance = new Ip();

        }

    }

    private function getByCur($i)
    {
        $p = 2052 + (intval($i) * 9);
        $offset = unpack("V", $this->data[4 + $p] . $this->data[5 + $p] . $this->data[6 + $p] . $this->data[7 + $p])[1];
        $length = unpack("C", $this->data[8 + $p])[1];
        return substr($this->data, $offset, $length);
    }

    public function get($ip)
    {
        $val = sprintf("%u", ip2long($ip));
        $ip_arr = explode('.', $ip);
        $pref = $ip_arr[0];
        $low = $this->prefStart[$pref];
        $high = $this->prefEnd[$pref];
        $cur = $low == $high ? $low : $this->Search($low, $high, $val);
        if ($cur == 100000000) {
            return "无信息";
        }
        return $this->getByCur($cur);
    }

    private function Search($low, $high, $k)
    {
        $M = 0;
        while ($low <= $high) {
            $mid = floor(($low + $high) / 2);
            $p = 2052 + ($mid * 9);

            $endipNum = unpack("V", $this->data, $p)[1];
            if ($endipNum >= $k) {
                $M = $mid;
                if ($mid == 0) {
                    break;
                }
                $high = $mid - 1;
            } else $low = $mid + 1;
        }
        return $M;
    }
}

require_once 'index.php'; // 引入包含MyClass定义的文件
$ipInstance = Ip::getInstance();
$startTime = microtime(true);

$ipInstance->get('1.0.3.0');

$endTime = microtime(true);
$executionTime = $endTime - $startTime;
echo "程序运行时间：" . number_format($executionTime, 4) . " 秒";

echo $ipInstance->get('1.0.3.0');
```

{% endtab %}
{% endtabs %}


# IPv6


# DAT format access code

IPv6 access code：

{% tabs %}
{% tab title="JavaScript" %}

```javascript
import java.io.IOException;
import java.math.BigInteger;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

public class Ip {

    private Map<Long, Long> prefStart = new HashMap<>();
    private Map<Long, Long> prefEnd = new HashMap<>();
    private BigInteger[] endArr;
    private String[] addrArr;

    private static Ip instance = null;

    private Ip(String filePath) throws IOException {
        Path path = Paths.get(filePath);

        byte[] data = null;
        try {
            data = Files.readAllBytes(path);
        } catch (IOException e) {
            throw new IOException("file not found" + e);
        }

        long numbers = unpackInt4byte(data[4], data[5], data[6], data[7]);

        for (int k = 0; k < numbers; k++) {
            int i = k * 12 + 4 + 4;
            prefStart.put(unpackInt4byte(data[i + 8], data[i + 9], data[i + 10], data[i + 11]), unpackInt4byte(data[i], data[i + 1], data[i + 2], data[i + 3]));

            prefEnd.put(unpackInt4byte(data[i + 8], data[i + 9], data[i + 10], data[i + 11]), unpackInt4byte(data[i + 4], data[i + 5], data[i + 6], data[i + 7]));

        }

        int recordSize = (int) unpackInt4byte(data[0], data[1], data[2], data[3]);
        endArr = new BigInteger[recordSize];
        addrArr = new String[recordSize];
        for (int i = 0; i < recordSize; i++) {
            int p = (int) numbers * 12 + 4 + 4 + (i * 55);
//            long endipnum = unpackInt4byte(data[p+50], data[1 + p+50], data[2 + p+50], data[3 + p+50]);

            int offset = (int) unpackInt4byte(data[50 + p], data[50 + p + 1], data[50 + p + 2], data[50 + p + 3]);
            int length = data[50 + p + 4] & 0xff;
            BigInteger endipnum = new BigInteger(new String(Arrays.copyOfRange(data, p, p + 50)).replaceAll("\\*", ""));
            endArr[i] = endipnum;

            addrArr[i] = new String(Arrays.copyOfRange(data, offset, (offset + length)));
        }

    }

    public static synchronized Ip getInstance(String path) throws IOException {
        if (instance == null)
            instance = new Ip(path);
        return instance;
    }

    public String get(String ip) {
        try {

            InetAddress inetAddress = InetAddress.getByName(ip);
            if (!(inetAddress instanceof Inet6Address)) {
                throw new IllegalArgumentException("Not an IPv6 address: " + ip);
            }
            ip = inetAddress.getHostAddress();
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid IPv6 address: " + ip, e);
        }
        if (ip.contains("%")) {
            ip = ip.split("%")[0];
        }
        String[] ips = ip.split("\\:");
        long pref = Long.parseLong(ips[0], 16);
        BigInteger val = stringToBigInt(ip);
        if (prefStart.get(pref) == null || prefEnd.get(pref) == null) {
            return "保留|保留|||||||||||||||||";
        }
        long low = prefStart.get(pref), high = prefEnd.get(pref);
        long cur = low == high ? low : search(low, high, val);
        return addrArr[(int) cur];

    }

    private int search(long low, long high, BigInteger k) {
        int M = 0;
        while (low <= high) {
            int mid = (int) (low + high) / 2;

            BigInteger endipNum = endArr[mid];
            if (endipNum.compareTo(k) == 0 || endipNum.compareTo(k) == 1) {
                M = mid;
                if (mid == 0) {
                    break;
                }
                high = mid - 1;
            } else
                low = mid + 1;
        }
        return M;
    }

    private long unpackInt4byte(byte a, byte b, byte c, byte d) {
        return (a & 0xFFL) | ((b << 8) & 0xFF00L) | ((c << 16) & 0xFF0000L) | ((d << 24) & 0xFF000000L);

    }

    public static BigInteger stringToBigInt(String ipInString) {
        ipInString = ipInString.replace(" ", "");
        byte[] bytes = ipv6ToBytes(ipInString);
        return new BigInteger(bytes);
    }

    public static byte[] ipv6ToBytes(String ipv6) {
        try {
            // 去除可能的区域索引（如fe80::1%eth0 -> fe80::1）
            String pureIpv6 = ipv6.split("%")[0];

            // 使用InetAddress解析
            InetAddress inetAddress = InetAddress.getByName(pureIpv6);

            if (!(inetAddress instanceof Inet6Address)) {
                throw new IllegalArgumentException("Not an IPv6 address: " + ipv6);
            }

            // 直接返回16字节的地址
            return inetAddress.getAddress();

        } catch (UnknownHostException e) {
            throw new IllegalArgumentException("Invalid IPv6 address: " + ipv6, e);
        }
    }

    public static void main(String[] args) throws IOException {
        Ip ip = Ip.getInstance("D:\\databases\\ipv6_city.dat");
        System.out.println(ip.get("240e:437:cb70:1650:5428:792c:bf57:932d%0"));

    }

}
```

{% endtab %}

{% tab title="Python" %}

```python
import mmap
import struct
import socket
import ipaddress


class IPV6Find:
    def __init__(self, file_name):
        self._handle = open(file_name, "rb")
        self.data = mmap.mmap(self._handle.fileno(), 0, access=mmap.ACCESS_READ)
        self.prefArr = {}
        record_size = self.unpack_int_4byte(0)
        self.numbers = self.unpack_int_4byte(4)
        i = 0
        while i < self.numbers:
            p = i * 12 + 4 + 4
            self.prefArr[self.unpack_int_4byte(p + 4 + 4)] = [self.unpack_int_4byte(p), self.unpack_int_4byte(p + 4)]
            i += 1

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        self.close()

    def close(self):
        self._handle.close()

    def get(self, ip):
        ipdot = ip.split(':')
        prefix = int(ipdot[0], 16)
        try:
            intIP = int(ipaddress.IPv6Address(ip))
        except:
            return "IPV6格式错误"
        # print(intIP)
        if prefix not in self.prefArr:
            return "未知1"
        low = self.prefArr[prefix][0]
        high = self.prefArr[prefix][1]

        cur = low if low == high else self.search(low, high, intIP)
        return self.get_arr(cur)

    def search(self, low, high, k):
        M = 0
        while low <= high:
            mid = (low + high) // 2
            p = self.numbers * 12 + 4 + 4 + (mid * 55)
            end_ip_num = int(self.data[p:p + 50].decode('utf-8').replace("*", ""))
            if end_ip_num >= k:
                M = mid
                if mid == 0:
                    break
                high = mid - 1
            else:
                low = mid + 1
        return M

    def ip_to_int(self, ip):
        _ip = socket.inet_aton(ip)
        return struct.unpack("!L", _ip)[0]

    def unpack_int_4byte(self, offset):
        return struct.unpack('<L', self.data[offset:offset + 4])[0]

    def unpack_int_1byte(self, offset):
        return struct.unpack('B', self.data[offset:offset + 1])[0]

    def get_arr(self, j):
        p = self.numbers * 12 + 4 + 4 + (j * 55)
        offset = self.unpack_int_4byte(p + 50)
        length = self.unpack_int_1byte(50 + p + 4)
        return self.data[offset:offset + length].decode('utf-8')


```

{% endtab %}

{% tab title="C#" %}

#### **IPSearch.cs**

```csharp
﻿using System.Net;
using System.Numerics;
using System.Text;

namespace newTest;

public class IPSearch
{
    private long[,] _prefMap;
    private byte[] _data;
    private readonly string _readMode;
    private readonly int _version;
    private FileStream _file;
    private long _numbers;

    private string _datPath = "D:\\databases\\ipv6_city.dat";

    private static readonly Lazy<IPSearch> lazy = new Lazy<IPSearch>(() => new IPSearch());

    public static IPSearch Instance
    {
        get { return lazy.Value; }
    }

    private IPSearch()
    {
        LoadDatV6();
    }


    private void LoadDatV6()
    {
        _file = new FileStream(_datPath, FileMode.Open, FileAccess.Read);
        _data = File.ReadAllBytes(_datPath);
        _file.Close();
        _numbers = UnpackInt4Byte(4);
        _prefMap = new long[65600, 2];

        for (int k = 0; k < _numbers; k++)
        {
            int i = k * 12 + 4 + 4;
            long startIndex = UnpackInt4Byte(i);
            long endIndex = UnpackInt4Byte(i + 4);
            long key = UnpackInt4Byte(i + 8);
            _prefMap[key, 0] = startIndex;
            _prefMap[key, 1] = endIndex;
        }
    }

    public string Find(string ip)
    {
        string[] ips = ip.Split(":");

        var addressBytes = IPAddress.Parse(ip).GetAddressBytes();
        BigInteger val = new BigInteger(addressBytes.Reverse().ToArray());
        long pref = Convert.ToInt64(ips[0], 16);
        long low = _prefMap[pref, 0], high = _prefMap[pref, 1];
        long cur = low == high ? low : BinarySearchV6(low, high, val);
        if (cur == 100000000)
        {
            return "|";
        }

        return cur > -1 ? GetAddrV6(cur) : "||||||||||";
    }

    private long BinarySearchV6(long low, long high, BigInteger k)
    {
        long M = 0;
        while (low <= high)
        {
            int mid = (int)(low + high) / 2;
            int p = (int)_numbers * 12 + 4 + 4 + (mid * 55);
            BigInteger endipNum;
            if (!BigInteger.TryParse(Encoding.UTF8.GetString(_data, p, 50).Replace("*", ""), out endipNum))
            {
                throw new Exception("转换biginteger失败");
            }

            if (endipNum.CompareTo(k) >= 0)
            {
                M = mid;
                if (mid == 0)
                {
                    break;
                }

                high = mid - 1;
            }
            else
                low = mid + 1;
        }

        return M;
    }

    private string GetAddrV6(long cur)
    {
        int p = (int)(_numbers * 12 + 4 + 4 + (cur * 55));
        int offset = (int)UnpackInt4Byte(p + 50);
        int length = UnpackInt1Byte(p + 50 + 4);
        return Encoding.UTF8.GetString(_data, offset, length);
    }


    private long UnpackInt4Byte(int offset)
    {
        return (uint)(_data[offset] | (_data[offset + 1] << 8) | (_data[offset + 2] << 16) | (_data[offset + 3] << 24));
    }

    private int UnpackInt1Byte(int offset)
    {
        return (_data[offset]);
    }
}
```

#### **Program.cs**

```csharp
﻿namespace newTest;

public class Test
{
    public static void Main()
    {
        // 1. Query object
        IPSearch search = IPSearch.Instance;

        // 2. Execute query operations
        string ip = "2404:be40:ffff:ffff:ffff:ffff:ffff:ffff";
        string res = search.Find(ip);
        Console.WriteLine(res);
    }
}
```

{% endtab %}

{% tab title="C++" %}

#### **endianness.h**

```cpp
#ifndef _ENDIANNESS_H_
#define _ENDIANNESS_H_
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
    defined(__BIG_ENDIAN__) ||                               \
    defined(__ARMEB__) ||                                    \
    defined(__THUMBEB__) ||                                  \
    defined(__AARCH64EB__) ||                                \
    defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
#ifndef __BIG_ENDIAN__
#define __BIG_ENDIAN__
#endif
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
    defined(__LITTLE_ENDIAN__) ||                                 \
    defined(__ARMEL__) ||                                         \
    defined(__THUMBEL__) ||                                       \
    defined(__AARCH64EL__) ||                                     \
    defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \
    defined(_WIN32) || defined(__i386__) || defined(__x86_64__) || \
    defined(_X86_) || defined(_IA64_)
#ifndef __LITTLE_ENDIAN__
#define __LITTLE_ENDIAN__
#endif
#else
#error "I don't know what architecture this is!"
#endif
#endif
```

#### **ipnews\_v6.cpp**

```cpp
#include "ipnews_v6.h"
#include "string.h"
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <sstream>
#include <bitset>
#include <array>
#include <iomanip>
#include <cstdint>
#include "uint128_t.h"


geo_ipv6 *ipv6_instance(const char *file_path) {
    static geo_ipv6 *ipv6_instance = NULL;
    if (ipv6_instance == NULL) {
        ipv6_instance = new geo_ipv6;
        if (ipv6_loadDat(ipv6_instance, file_path) >= 0) {
            return ipv6_instance;
        }

        if (ipv6_instance == NULL) {
            delete(ipv6_instance);
        }
        return NULL;
    }
    return ipv6_instance;
}

int deleteChar(char *s, char c) {
    if (NULL == s) {
        return -1;
    } else {
        char *f = s;
        int i = 0, j = 0;
        while (*s) {
            i++;
            if (*s != c) {
                j++;
                *f = *s;
                f++;
            }
            s++;
        }
        *f = '\0';
        if (i == j)
            return 0;
        return i - j;
    }
}

char *get_addr(geo_ipv6 *p, uint32_t cur) {
    uint32_t offset, length;
    uint32_t j = p->numbers * 12 + 4 + 4 + (cur * 55);
    offset = ipv6_read_int32(p, j + 50);
    length = (uint32_t) p->data[4 + j + 50];
    char *result = (char *) malloc((length + 1) * sizeof(char));
    memcpy(result, p->data + offset, length);
    result[length] = '\0';
    return result;
}


int32_t ipv6_loadDat(geo_ipv6 *p, const char *file_path) {
    FILE *file;
    uint32_t len = 0;
    uint32_t k;
    int i, j;
    uint32_t offset, length;
    file = fopen(file_path, "rb");
    if (file == NULL) {
        printf("%s", "There is no such file or directory");
        return -2;
    }
    fseek(file, 0, SEEK_END);
    len = ftell(file);
    fseek(file, 0, SEEK_SET);
    p->data = (uint8_t *) malloc(len * sizeof(uint8_t));
    fread(p->data, 1, len, file);
    fclose(file);

    p->numbers = ipv6_read_int32(p, 4);
    memset(p->prefStart, 0, sizeof(p->prefStart));
    memset(p->prefEnd, 0, sizeof(p->prefEnd));

    for (k = 0; k < p->numbers; k++) {
        i = k * 12 + 4 + 4;
        p->prefStart[ipv6_read_int32(p, i + 8)] = ipv6_read_int32(p, i);
        p->prefEnd[ipv6_read_int32(p, i + 8)] = ipv6_read_int32(p, i + 4);
    }

    return 0;
}

std::array<std::string, 8> split(const char *str, char delimiter) {
    std::array<std::string, 8> parts;
    size_t i = 0;
    std::stringstream ss(str);
    std::string token;

    while (std::getline(ss, token, delimiter)) {
        if (i < parts.size()) {
            parts[i++] = token;
        }
    }
    return parts;
}

long getPref(const char *ip) {
    std::string str_ip(ip);
    size_t index = str_ip.find(':');
    if (index == std::string::npos) {
        return strtoll(ip, nullptr, 16); 
    }
    std::string prefix = str_ip.substr(0, index);
    long pref = strtoll(prefix.c_str(), nullptr, 16);

    return pref;
}

uint128_t ipv6ToInt(const char *ip) {
    std::string expandedIp = ip;

    size_t pos = expandedIp.find("::");
    if (pos != std::string::npos) {
        std::string firstPart = expandedIp.substr(0, pos);
        std::string secondPart = expandedIp.substr(pos + 2); 

        std::vector<std::string> firstBlocks;
        std::vector<std::string> secondBlocks;

        std::istringstream firstStream(firstPart);
        std::istringstream secondStream(secondPart);
        std::string block;

        while (std::getline(firstStream, block, ':')) {
            if (!block.empty()) {
                firstBlocks.push_back(block);
            }
        }
        while (std::getline(secondStream, block, ':')) {
            if (!block.empty()) {
                secondBlocks.push_back(block);
            }
        }

        int totalBlocks = 8;
        int existingBlocks = firstBlocks.size() + secondBlocks.size();
        int missingBlocks = totalBlocks - existingBlocks;

        std::ostringstream resultStream;
        for (size_t i = 0; i < firstBlocks.size(); ++i) {
            resultStream << firstBlocks[i];
            if (i < firstBlocks.size() - 1 || missingBlocks > 0 || !secondBlocks.empty()) {
                resultStream << ":";
            }
        }
        for (int i = 0; i < missingBlocks; ++i) {
            if (i > 0 || !secondBlocks.empty()) {
                resultStream << ":";
            }
            resultStream << "0000";
        }
        for (size_t i = 0; i < secondBlocks.size(); ++i) {
            if (i > 0 || missingBlocks > 0) {
                resultStream << ":";
            }
            resultStream << secondBlocks[i];
        }

        expandedIp = resultStream.str();
    }

    std::istringstream stream(expandedIp);
    std::string block;
    std::ostringstream finalStream;
    while (std::getline(stream, block, ':')) {
        if (block.length() < 4) {
            block.insert(0, 4 - block.length(), '0');
        }
        finalStream << block;
        if (stream.peek() != EOF) {
            finalStream << ":";
        }
    }
    std::vector<uint16_t> groups;
    std::stringstream ss(expandedIp);
    std::string segment;

    while (std::getline(ss, segment, ':')) {
        if (segment.empty()) continue;
        if (segment.length() > 4) {
            std::cout<< expandedIp << std::endl;
            throw std::invalid_argument("Invalid IPv6 address segment");
        }
        uint16_t value = std::stoi(segment, nullptr, 16);
        groups.push_back(value);
    }

    if (groups.size() != 8) {
        std::cout<< expandedIp << std::endl;
        throw std::invalid_argument("Invalid IPv6 address");
    }

    uint64_t upper = 0;
    uint64_t lower = 0;

    for (size_t i = 0; i < 8; ++i) {
        uint64_t value = groups[i];
        if (i < 4) {
            upper = (upper << 16) | value;
        } else {
            lower = (lower << 16) | value;
        }
    }

    return uint128_t(upper, lower);
}

char *ipv6_query(geo_ipv6 *p, const char *ip) {
    uint32_t pref, cur, low, high;
    uint128_t intIP;
    if (NULL == p) {
        return NULL;
    }

    pref = getPref(ip);
    intIP = ipv6ToInt(ip);
    low = p->prefStart[pref];
    high = p->prefEnd[pref];
    if (low == 0 && high == 0) {
        const char *text = "||保留|保留|||||||||||||||";
        char *str = (char *) malloc(strlen(text) + 1); 
        if (str) {
            strcpy(str, text); 
        }
        return str; 
    }
    cur = (low == high) ? low : ipv6_binary_search(p, low, high, intIP);
    return get_addr(p, cur);
}

long ipv6_binary_search(geo_ipv6 *p, long low, long high, uint128_t k) {
    long M = 0;
    char *resultEnd = (char *) malloc((50) * sizeof(char));

    while (low <= high) {
        long mid = (low + high) / 2;
        int j = p->numbers * 12 + 4 + 4 + (mid * 55);
        memset(resultEnd, '\0', 50 * sizeof(char));
        memcpy(resultEnd, p->data + j, 50);
        resultEnd[49] = '\0';
        deleteChar(resultEnd,'*');
        uint128_t endipNum = uint128_t(resultEnd, 10);
        if (endipNum >= k) {
            M = mid;
            if (mid == 0) {
                break;
            }
            high = mid - 1;
        } else
            low = mid + 1;
    }
    free(resultEnd);
    resultEnd = NULL;
    return M;
}


uint32_t ipv6_read_int32(geo_ipv6 *p, int pos) {
    uint32_t result;
    result = (uint32_t) ((p->data[pos]) | (p->data[pos + 1] << 8) | (p->data[pos + 2] << 16) | (p->data[pos + 3] << 24));
    return result;
}
```

#### **ipnews\_v6.h**

```cpp
#ifndef __IP_H6_
#define __IP_H6_

#include <stdint.h>
#include <stdlib.h>
#include "uint128_t.h"

#ifdef __cplusplus
extern "C" {
#endif

	typedef struct tag_ipv6
	{
		uint32_t prefStart[65600];
		uint32_t prefEnd[65600];
		uint8_t* data;
		long numbers;
	}geo_ipv6;

	geo_ipv6* ipv6_instance(const char* file_path);
	int32_t ipv6_loadDat(geo_ipv6* p,const char* file_path);
	long getPref(const char* ip);
	char* ipv6_query(geo_ipv6* p, const char *ip);
	long ipv6_binary_search(geo_ipv6* p, long low, long high, uint128_t k);
	uint32_t ipv6_read_int32(geo_ipv6* p, int pos);


#ifdef __cplusplus
}
#endif
#endif
```

#### **main.cpp**

```cpp
#include "ipnews_v6.h"
#include <memory>
#include <iostream>


int main() {
    std::unique_ptr<geo_ipv6> finder_v6(ipv6_instance("file_path_v6"));
    const char* ip = "ip_address";
    char* result = ipv6_query(finder_v6.get(), ip);
    std::cout << result << std::endl;
    free(result);
    result = NULL;
}
```

#### **uint128\_t.build**

```cpp
// IMPLEMENTATION BUILD HEADER
#ifndef _UINT128_T_BUILD
  #define _UINT128_T_BUILD
  #include "uint128_t_config.include"
  #define UINT128_T_EXTERN _UINT128_T_EXPORT
#endif
#include "uint128_t.include"
```

#### **uint128\_t.cpp**

```cpp
#include "uint128_t.build"

#include <algorithm>
#include <cctype>
#include <sstream>

const uint128_t uint128_0(0);
const uint128_t uint128_1(1);

uint128_t::uint128_t(const std::string & s, uint8_t base) {
    init(s.c_str(), s.size(), base);
}

uint128_t::uint128_t(const char *s, const std::size_t len, uint8_t base) {
    init(s, len, base);
}

uint128_t::uint128_t(const bool & b)
    : uint128_t((uint8_t) b)
{}

void uint128_t::init(const char *s, std::size_t len, uint8_t base) {
    if ((s == NULL) || !len || (s[0] == '\x00')){
        LOWER = UPPER = 0;
        return;
    }

    while (*s && len && std::isspace(*s)) {
        ++s;
        len--;
    }

    // no prefixes
    switch (base) {
        case 16:
            _init_hex(s, len);
            break;
        case 10:
            _init_dec(s, len);
            break;
        case 8:
            _init_oct(s, len);
            break;
        case 2:
            _init_bin(s, len);
            break;
        default:
            // should probably throw error here
            break;
    }
}

void uint128_t::_init_hex(const char *s, std::size_t len) {
    // 2**128 = 0x100000000000000000000000000000000.
    static const std::size_t MAX_LEN = 32;

    LOWER = UPPER = 0;
    if (!s || !len) {
        return;
    }

    const std::size_t max_len = std::min(len, MAX_LEN);
    const std::size_t starting_index = (MAX_LEN < len)?(len - MAX_LEN):0;
    const std::size_t double_lower = sizeof(LOWER) * 2;
    const std::size_t lower_len = (max_len >= double_lower)?double_lower:max_len;
    const std::size_t upper_len = (max_len >= double_lower)?(max_len - double_lower):0;

    std::stringstream lower_s, upper_s;
    upper_s << std::hex << std::string(s + starting_index, upper_len);
    lower_s << std::hex << std::string(s + starting_index + upper_len, lower_len);

    // should check for errors
    upper_s >> UPPER;
    lower_s >> LOWER;
}

void uint128_t::_init_dec(const char *s, std::size_t len){
    // 2**128 = 340282366920938463463374607431768211456.
    static const std::size_t MAX_LEN = 39;

    LOWER = UPPER = 0;
    if (!s || !len) {
        return;
    }

    const std::size_t max_len = std::min(len, MAX_LEN);
    const std::size_t starting_index = (MAX_LEN < len)?(len - MAX_LEN):0;
    s += starting_index;

    for (std::size_t i = 0; *s && ('0' <= *s) && (*s <= '9') && (i < max_len); ++s, ++i){
        *this *= 10;
        *this += *s - '0';
    }
}

void uint128_t::_init_oct(const char *s, std::size_t len){
    // 2**128 = 0o4000000000000000000000000000000000000000000.
    static const std::size_t MAX_LEN = 43;

    LOWER = UPPER = 0;
    if (!s || !len) {
        return;
    }

    const std::size_t max_len = std::min(len, MAX_LEN);
    const std::size_t starting_index = (MAX_LEN < len)?(len - MAX_LEN):0;
    s += starting_index;

    for (std::size_t i = 0; *s && ('0' <= *s) && (*s <= '7') && (i < max_len); ++s, ++i){
        *this *= 8;
        *this += *s - '0';
    }
}

void uint128_t::_init_bin(const char *s, std::size_t len){
    // 2**128 = 0b100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
    static const std::size_t MAX_LEN = 128;

    LOWER = UPPER = 0;
    if (!s || !len) {
        return;
    }

    const std::size_t max_len = std::min(len, MAX_LEN);
    const std::size_t starting_index = (MAX_LEN < len)?(len - MAX_LEN):0;
    const std::size_t eight_lower = sizeof(LOWER) * 8;
    const std::size_t lower_len = (max_len >= eight_lower)?eight_lower:max_len;
    const std::size_t upper_len = (max_len >= eight_lower)?(max_len - eight_lower):0;
    s += starting_index;

    for (std::size_t i = 0; *s && ('0' <= *s) && (*s <= '1') && (i < upper_len); ++s, ++i){
        UPPER <<= 1;
        UPPER |= *s - '0';
    }

    for (std::size_t i = 0; *s && ('0' <= *s) && (*s <= '1') && (i < lower_len); ++s, ++i){
        LOWER <<= 1;
        LOWER |= *s - '0';
    }
}

uint128_t & uint128_t::operator=(const bool & rhs) {
    UPPER = 0;
    LOWER = rhs;
    return *this;
}

uint128_t::operator bool() const{
    return (bool) (UPPER | LOWER);
}

uint128_t::operator uint8_t() const{
    return (uint8_t) LOWER;
}

uint128_t::operator uint16_t() const{
    return (uint16_t) LOWER;
}

uint128_t::operator uint32_t() const{
    return (uint32_t) LOWER;
}

uint128_t::operator uint64_t() const{
    return (uint64_t) LOWER;
}

uint128_t uint128_t::operator&(const uint128_t & rhs) const{
    return uint128_t(UPPER & rhs.UPPER, LOWER & rhs.LOWER);
}

uint128_t & uint128_t::operator&=(const uint128_t & rhs){
    UPPER &= rhs.UPPER;
    LOWER &= rhs.LOWER;
    return *this;
}

uint128_t uint128_t::operator|(const uint128_t & rhs) const{
    return uint128_t(UPPER | rhs.UPPER, LOWER | rhs.LOWER);
}

uint128_t & uint128_t::operator|=(const uint128_t & rhs){
    UPPER |= rhs.UPPER;
    LOWER |= rhs.LOWER;
    return *this;
}

uint128_t uint128_t::operator^(const uint128_t & rhs) const{
    return uint128_t(UPPER ^ rhs.UPPER, LOWER ^ rhs.LOWER);
}

uint128_t & uint128_t::operator^=(const uint128_t & rhs){
    UPPER ^= rhs.UPPER;
    LOWER ^= rhs.LOWER;
    return *this;
}

uint128_t uint128_t::operator~() const{
    return uint128_t(~UPPER, ~LOWER);
}

uint128_t uint128_t::operator<<(const uint128_t & rhs) const{
    const uint64_t shift = rhs.LOWER;
    if (((bool) rhs.UPPER) || (shift >= 128)){
        return uint128_0;
    }
    else if (shift == 64){
        return uint128_t(LOWER, 0);
    }
    else if (shift == 0){
        return *this;
    }
    else if (shift < 64){
        return uint128_t((UPPER << shift) + (LOWER >> (64 - shift)), LOWER << shift);
    }
    else if ((128 > shift) && (shift > 64)){
        return uint128_t(LOWER << (shift - 64), 0);
    }
    else{
        return uint128_0;
    }
}

uint128_t & uint128_t::operator<<=(const uint128_t & rhs){
    *this = *this << rhs;
    return *this;
}

uint128_t uint128_t::operator>>(const uint128_t & rhs) const{
    const uint64_t shift = rhs.LOWER;
    if (((bool) rhs.UPPER) || (shift >= 128)){
        return uint128_0;
    }
    else if (shift == 64){
        return uint128_t(0, UPPER);
    }
    else if (shift == 0){
        return *this;
    }
    else if (shift < 64){
        return uint128_t(UPPER >> shift, (UPPER << (64 - shift)) + (LOWER >> shift));
    }
    else if ((128 > shift) && (shift > 64)){
        return uint128_t(0, (UPPER >> (shift - 64)));
    }
    else{
        return uint128_0;
    }
}

uint128_t & uint128_t::operator>>=(const uint128_t & rhs){
    *this = *this >> rhs;
    return *this;
}

bool uint128_t::operator!() const{
    return !(bool) (UPPER | LOWER);
}

bool uint128_t::operator&&(const uint128_t & rhs) const{
    return ((bool) *this && rhs);
}

bool uint128_t::operator||(const uint128_t & rhs) const{
     return ((bool) *this || rhs);
}

bool uint128_t::operator==(const uint128_t & rhs) const{
    return ((UPPER == rhs.UPPER) && (LOWER == rhs.LOWER));
}

bool uint128_t::operator!=(const uint128_t & rhs) const{
    return ((UPPER != rhs.UPPER) | (LOWER != rhs.LOWER));
}

bool uint128_t::operator>(const uint128_t & rhs) const{
    if (UPPER == rhs.UPPER){
        return (LOWER > rhs.LOWER);
    }
    return (UPPER > rhs.UPPER);
}

bool uint128_t::operator<(const uint128_t & rhs) const{
    if (UPPER == rhs.UPPER){
        return (LOWER < rhs.LOWER);
    }
    return (UPPER < rhs.UPPER);
}

bool uint128_t::operator>=(const uint128_t & rhs) const{
    return ((*this > rhs) | (*this == rhs));
}

bool uint128_t::operator<=(const uint128_t & rhs) const{
    return ((*this < rhs) | (*this == rhs));
}

uint128_t uint128_t::operator+(const uint128_t & rhs) const{
    return uint128_t(UPPER + rhs.UPPER + ((LOWER + rhs.LOWER) < LOWER), LOWER + rhs.LOWER);
}

uint128_t & uint128_t::operator+=(const uint128_t & rhs){
    UPPER += rhs.UPPER + ((LOWER + rhs.LOWER) < LOWER);
    LOWER += rhs.LOWER;
    return *this;
}

uint128_t uint128_t::operator-(const uint128_t & rhs) const{
    return uint128_t(UPPER - rhs.UPPER - ((LOWER - rhs.LOWER) > LOWER), LOWER - rhs.LOWER);
}

uint128_t & uint128_t::operator-=(const uint128_t & rhs){
    *this = *this - rhs;
    return *this;
}

uint128_t uint128_t::operator*(const uint128_t & rhs) const{
    // split values into 4 32-bit parts
    uint64_t top[4] = {UPPER >> 32, UPPER & 0xffffffff, LOWER >> 32, LOWER & 0xffffffff};
    uint64_t bottom[4] = {rhs.UPPER >> 32, rhs.UPPER & 0xffffffff, rhs.LOWER >> 32, rhs.LOWER & 0xffffffff};
    uint64_t products[4][4];

    // multiply each component of the values
    for(int y = 3; y > -1; y--){
        for(int x = 3; x > -1; x--){
            products[3 - x][y] = top[x] * bottom[y];
        }
    }

    // first row
    uint64_t fourth32 = (products[0][3] & 0xffffffff);
    uint64_t third32  = (products[0][2] & 0xffffffff) + (products[0][3] >> 32);
    uint64_t second32 = (products[0][1] & 0xffffffff) + (products[0][2] >> 32);
    uint64_t first32  = (products[0][0] & 0xffffffff) + (products[0][1] >> 32);

    // second row
    third32  += (products[1][3] & 0xffffffff);
    second32 += (products[1][2] & 0xffffffff) + (products[1][3] >> 32);
    first32  += (products[1][1] & 0xffffffff) + (products[1][2] >> 32);

    // third row
    second32 += (products[2][3] & 0xffffffff);
    first32  += (products[2][2] & 0xffffffff) + (products[2][3] >> 32);

    // fourth row
    first32  += (products[3][3] & 0xffffffff);

    // move carry to next digit
    third32  += fourth32 >> 32;
    second32 += third32  >> 32;
    first32  += second32 >> 32;

    // remove carry from current digit
    fourth32 &= 0xffffffff;
    third32  &= 0xffffffff;
    second32 &= 0xffffffff;
    first32  &= 0xffffffff;

    // combine components
    return uint128_t((first32 << 32) | second32, (third32 << 32) | fourth32);
}

uint128_t & uint128_t::operator*=(const uint128_t & rhs){
    *this = *this * rhs;
    return *this;
}

void uint128_t::ConvertToVector(std::vector<uint8_t> & ret, const uint64_t & val) const {
    ret.push_back(static_cast<uint8_t>(val >> 56));
    ret.push_back(static_cast<uint8_t>(val >> 48));
    ret.push_back(static_cast<uint8_t>(val >> 40));
    ret.push_back(static_cast<uint8_t>(val >> 32));
    ret.push_back(static_cast<uint8_t>(val >> 24));
    ret.push_back(static_cast<uint8_t>(val >> 16));
    ret.push_back(static_cast<uint8_t>(val >> 8));
    ret.push_back(static_cast<uint8_t>(val));
}

void uint128_t::export_bits(std::vector<uint8_t> &ret) const {
    ConvertToVector(ret, const_cast<const uint64_t&>(UPPER));
    ConvertToVector(ret, const_cast<const uint64_t&>(LOWER));
}

std::pair <uint128_t, uint128_t> uint128_t::divmod(const uint128_t & lhs, const uint128_t & rhs) const{
    // Save some calculations /////////////////////
    if (rhs == uint128_0){
        throw std::domain_error("Error: division or modulus by 0");
    }
    else if (rhs == uint128_1){
        return std::pair <uint128_t, uint128_t> (lhs, uint128_0);
    }
    else if (lhs == rhs){
        return std::pair <uint128_t, uint128_t> (uint128_1, uint128_0);
    }
    else if ((lhs == uint128_0) || (lhs < rhs)){
        return std::pair <uint128_t, uint128_t> (uint128_0, lhs);
    }

    std::pair <uint128_t, uint128_t> qr (uint128_0, uint128_0);
    for(uint8_t x = lhs.bits(); x > 0; x--){
        qr.first  <<= uint128_1;
        qr.second <<= uint128_1;

        if ((lhs >> (x - 1U)) & 1){
            ++qr.second;
        }

        if (qr.second >= rhs){
            qr.second -= rhs;
            ++qr.first;
        }
    }
    return qr;
}

uint128_t uint128_t::operator/(const uint128_t & rhs) const{
    return divmod(*this, rhs).first;
}

uint128_t & uint128_t::operator/=(const uint128_t & rhs){
    *this = *this / rhs;
    return *this;
}

uint128_t uint128_t::operator%(const uint128_t & rhs) const{
    return divmod(*this, rhs).second;
}

uint128_t & uint128_t::operator%=(const uint128_t & rhs){
    *this = *this % rhs;
    return *this;
}

uint128_t & uint128_t::operator++(){
    return *this += uint128_1;
}

uint128_t uint128_t::operator++(int){
    uint128_t temp(*this);
    ++*this;
    return temp;
}

uint128_t & uint128_t::operator--(){
    return *this -= uint128_1;
}

uint128_t uint128_t::operator--(int){
    uint128_t temp(*this);
    --*this;
    return temp;
}

uint128_t uint128_t::operator+() const{
    return *this;
}

uint128_t uint128_t::operator-() const{
    return ~*this + uint128_1;
}

const uint64_t & uint128_t::upper() const{
    return UPPER;
}

const uint64_t & uint128_t::lower() const{
    return LOWER;
}

uint8_t uint128_t::bits() const{
    uint8_t out = 0;
    if (UPPER){
        out = 64;
        uint64_t up = UPPER;
        while (up){
            up >>= 1;
            out++;
        }
    }
    else{
        uint64_t low = LOWER;
        while (low){
            low >>= 1;
            out++;
        }
    }
    return out;
}

std::string uint128_t::str(uint8_t base, const unsigned int & len) const{
    if ((base < 2) || (base > 16)){
        throw std::invalid_argument("Base must be in the range [2, 16]");
    }
    std::string out = "";
    if (!(*this)){
        out = "0";
    }
    else{
        std::pair <uint128_t, uint128_t> qr(*this, uint128_0);
        do{
            qr = divmod(qr.first, base);
            out = "0123456789abcdef"[(uint8_t) qr.second] + out;
        } while (qr.first);
    }
    if (out.size() < len){
        out = std::string(len - out.size(), '0') + out;
    }
    return out;
}

uint128_t operator<<(const bool & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator<<(const uint8_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator<<(const uint16_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator<<(const uint32_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator<<(const uint64_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator<<(const int8_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator<<(const int16_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator<<(const int32_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator<<(const int64_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) << rhs;
}

uint128_t operator>>(const bool & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

uint128_t operator>>(const uint8_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

uint128_t operator>>(const uint16_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

uint128_t operator>>(const uint32_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

uint128_t operator>>(const uint64_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

uint128_t operator>>(const int8_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

uint128_t operator>>(const int16_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

uint128_t operator>>(const int32_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

uint128_t operator>>(const int64_t & lhs, const uint128_t & rhs){
    return uint128_t(lhs) >> rhs;
}

std::ostream & operator<<(std::ostream & stream, const uint128_t & rhs){
    if (stream.flags() & stream.oct){
        stream << rhs.str(8);
    }
    else if (stream.flags() & stream.dec){
        stream << rhs.str(10);
    }
    else if (stream.flags() & stream.hex){
        stream << rhs.str(16);
    }
    return stream;
}
```

#### **uint128\_t.h**

```cpp
// PUBLIC IMPORT HEADER
#ifndef _UINT128_H_
#define _UINT128_H_
#include "uint128_t_config.include"
#define UINT128_T_EXTERN _UINT128_T_IMPORT
#include "uint128_t.include"
#endif
```

#### **uint128\_t.include**

```cpp
/*
uint128_t.h
An unsigned 128 bit integer type for C++

Copyright (c) 2013 - 2017 Jason Lee @ calccrypto at gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

With much help from Auston Sterling

Thanks to Stefan Deigmüller for finding
a bug in operator*.

Thanks to François Dessenne for convincing me
to do a general rewrite of this class.
*/

#ifndef __UINT128_T__
#define __UINT128_T__

#include <cstdint>
#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "endianness.h"

class UINT128_T_EXTERN uint128_t;

// Give uint128_t type traits
namespace std {  // This is probably not a good idea
    template <> struct is_arithmetic <uint128_t> : std::true_type {};
    template <> struct is_integral   <uint128_t> : std::true_type {};
    template <> struct is_unsigned   <uint128_t> : std::true_type {};
}

class uint128_t{
    private:
#ifdef __BIG_ENDIAN__
        uint64_t UPPER, LOWER;
#endif
#ifdef __LITTLE_ENDIAN__
        uint64_t LOWER, UPPER;
#endif

    public:
        // Constructors
        uint128_t() = default;
        uint128_t(const uint128_t & rhs) = default;
        uint128_t(uint128_t && rhs) = default;

        // do not use prefixes (0x, 0b, etc.)
        // if the input string is too long, only right most characters are read
        uint128_t(const std::string & s, uint8_t base);
        uint128_t(const char *s, std::size_t len, uint8_t base);

        uint128_t(const bool & b);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t(const T & rhs)
#ifdef __BIG_ENDIAN__
            : UPPER(0), LOWER(rhs)
#endif
#ifdef __LITTLE_ENDIAN__
            : LOWER(rhs), UPPER(0)
#endif
        {
            if (std::is_signed<T>::value) {
                if (rhs < 0) {
                    UPPER = -1;
                }
            }
        }

        template <typename S, typename T, typename = typename std::enable_if <std::is_integral<S>::value && std::is_integral<T>::value, void>::type>
        uint128_t(const S & upper_rhs, const T & lower_rhs)
#ifdef __BIG_ENDIAN__
            : UPPER(upper_rhs), LOWER(lower_rhs)
#endif
#ifdef __LITTLE_ENDIAN__
            : LOWER(lower_rhs), UPPER(upper_rhs)
#endif
        {}

        //  RHS input args only

        // Assignment Operator
        uint128_t & operator=(const uint128_t & rhs) = default;
        uint128_t & operator=(uint128_t && rhs) = default;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator=(const T & rhs){
            UPPER = 0;

            if (std::is_signed<T>::value) {
                if (rhs < 0) {
                    UPPER = -1;
                }
            }

            LOWER = rhs;
            return *this;
        }

        uint128_t & operator=(const bool & rhs);

        // Typecast Operators
        operator bool() const;
        operator uint8_t() const;
        operator uint16_t() const;
        operator uint32_t() const;
        operator uint64_t() const;

        // Bitwise Operators
        uint128_t operator&(const uint128_t & rhs) const;

        void export_bits(std::vector<uint8_t> & ret) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator&(const T & rhs) const{
            return uint128_t(0, LOWER & (uint64_t) rhs);
        }

        uint128_t & operator&=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator&=(const T & rhs){
            UPPER = 0;
            LOWER &= rhs;
            return *this;
        }

        uint128_t operator|(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator|(const T & rhs) const{
            return uint128_t(UPPER, LOWER | (uint64_t) rhs);
        }

        uint128_t & operator|=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator|=(const T & rhs){
            LOWER |= (uint64_t) rhs;
            return *this;
        }

        uint128_t operator^(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator^(const T & rhs) const{
            return uint128_t(UPPER, LOWER ^ (uint64_t) rhs);
        }

        uint128_t & operator^=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator^=(const T & rhs){
            LOWER ^= (uint64_t) rhs;
            return *this;
        }

        uint128_t operator~() const;

        // Bit Shift Operators
        uint128_t operator<<(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator<<(const T & rhs) const{
            return *this << uint128_t(rhs);
        }

        uint128_t & operator<<=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator<<=(const T & rhs){
            *this = *this << uint128_t(rhs);
            return *this;
        }

        uint128_t operator>>(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator>>(const T & rhs) const{
            return *this >> uint128_t(rhs);
        }

        uint128_t & operator>>=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator>>=(const T & rhs){
            *this = *this >> uint128_t(rhs);
            return *this;
        }

        // Logical Operators
        bool operator!() const;
        bool operator&&(const uint128_t & rhs) const;
        bool operator||(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        bool operator&&(const T & rhs) const{
            return static_cast <bool> (*this && rhs);
        }

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        bool operator||(const T & rhs) const{
            return static_cast <bool> (*this || rhs);
        }

        // Comparison Operators
        bool operator==(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        bool operator==(const T & rhs) const{
            return (!UPPER && (LOWER == (uint64_t) rhs));
        }

        bool operator!=(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        bool operator!=(const T & rhs) const{
            return (UPPER | (LOWER != (uint64_t) rhs));
        }

        bool operator>(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        bool operator>(const T & rhs) const{
            return (UPPER || (LOWER > (uint64_t) rhs));
        }

        bool operator<(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        bool operator<(const T & rhs) const{
            return (!UPPER)?(LOWER < (uint64_t) rhs):false;
        }

        bool operator>=(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        bool operator>=(const T & rhs) const{
            return ((*this > rhs) | (*this == rhs));
        }

        bool operator<=(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        bool operator<=(const T & rhs) const{
            return ((*this < rhs) | (*this == rhs));
        }

        // Arithmetic Operators
        uint128_t operator+(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator+(const T & rhs) const{
            return uint128_t(UPPER + ((LOWER + (uint64_t) rhs) < LOWER), LOWER + (uint64_t) rhs);
        }

        uint128_t & operator+=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator+=(const T & rhs){
            return *this += uint128_t(rhs);
        }

        uint128_t operator-(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator-(const T & rhs) const{
            return uint128_t((uint64_t) (UPPER - ((LOWER - rhs) > LOWER)), (uint64_t) (LOWER - rhs));
        }

        uint128_t & operator-=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator-=(const T & rhs){
            return *this = *this - uint128_t(rhs);
        }

        uint128_t operator*(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator*(const T & rhs) const{
            return *this * uint128_t(rhs);
        }

        uint128_t & operator*=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator*=(const T & rhs){
            return *this = *this * uint128_t(rhs);
        }

    private:
        std::pair <uint128_t, uint128_t> divmod(const uint128_t & lhs, const uint128_t & rhs) const;
        void ConvertToVector(std::vector<uint8_t> & current, const uint64_t & val) const;
        // do not use prefixes (0x, 0b, etc.)
        // if the input string is too long, only right most characters are read
        void init(const char * s, std::size_t len, uint8_t base);
        void _init_hex(const char *s, std::size_t len);
        void _init_dec(const char *s, std::size_t len);
        void _init_oct(const char *s, std::size_t len);
        void _init_bin(const char *s, std::size_t len);

    public:
        uint128_t operator/(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator/(const T & rhs) const{
            return *this / uint128_t(rhs);
        }

        uint128_t & operator/=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator/=(const T & rhs){
            return *this = *this / uint128_t(rhs);
        }

        uint128_t operator%(const uint128_t & rhs) const;

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t operator%(const T & rhs) const{
            return *this % uint128_t(rhs);
        }

        uint128_t & operator%=(const uint128_t & rhs);

        template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
        uint128_t & operator%=(const T & rhs){
            return *this = *this % uint128_t(rhs);
        }

        // Increment Operator
        uint128_t & operator++();
        uint128_t operator++(int);

        // Decrement Operator
        uint128_t & operator--();
        uint128_t operator--(int);

        // Nothing done since promotion doesn't work here
        uint128_t operator+() const;

        // two's complement
        uint128_t operator-() const;

        // Get private values
        const uint64_t & upper() const;
        const uint64_t & lower() const;

        // Get bitsize of value
        uint8_t bits() const;

        // Get string representation of value
        std::string str(uint8_t base = 10, const unsigned int & len = 0) const;
};

// useful values
UINT128_T_EXTERN extern const uint128_t uint128_0;
UINT128_T_EXTERN extern const uint128_t uint128_1;

// lhs type T as first arguemnt
// If the output is not a bool, casts to type T

// Bitwise Operators
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
uint128_t operator&(const T & lhs, const uint128_t & rhs){
    return rhs & lhs;
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator&=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (rhs & lhs);
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
uint128_t operator|(const T & lhs, const uint128_t & rhs){
    return rhs | lhs;
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator|=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (rhs | lhs);
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
uint128_t operator^(const T & lhs, const uint128_t & rhs){
    return rhs ^ lhs;
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator^=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (rhs ^ lhs);
}

// Bitshift operators
UINT128_T_EXTERN uint128_t operator<<(const bool     & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator<<(const uint8_t  & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator<<(const uint16_t & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator<<(const uint32_t & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator<<(const uint64_t & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator<<(const int8_t   & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator<<(const int16_t  & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator<<(const int32_t  & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator<<(const int64_t  & lhs, const uint128_t & rhs);

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator<<=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (uint128_t(lhs) << rhs);
}

UINT128_T_EXTERN uint128_t operator>>(const bool     & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator>>(const uint8_t  & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator>>(const uint16_t & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator>>(const uint32_t & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator>>(const uint64_t & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator>>(const int8_t   & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator>>(const int16_t  & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator>>(const int32_t  & lhs, const uint128_t & rhs);
UINT128_T_EXTERN uint128_t operator>>(const int64_t  & lhs, const uint128_t & rhs);

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator>>=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (uint128_t(lhs) >> rhs);
}

// Comparison Operators
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
bool operator==(const T & lhs, const uint128_t & rhs){
    return (!rhs.upper() && ((uint64_t) lhs == rhs.lower()));
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
bool operator!=(const T & lhs, const uint128_t & rhs){
    return (rhs.upper() | ((uint64_t) lhs != rhs.lower()));
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
bool operator>(const T & lhs, const uint128_t & rhs){
    return (!rhs.upper()) && ((uint64_t) lhs > rhs.lower());
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
bool operator<(const T & lhs, const uint128_t & rhs){
    if (rhs.upper()){
        return true;
    }
    return ((uint64_t) lhs < rhs.lower());
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
bool operator>=(const T & lhs, const uint128_t & rhs){
    if (rhs.upper()){
        return false;
    }
    return ((uint64_t) lhs >= rhs.lower());
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
bool operator<=(const T & lhs, const uint128_t & rhs){
    if (rhs.upper()){
        return true;
    }
    return ((uint64_t) lhs <= rhs.lower());
}

// Arithmetic Operators
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
uint128_t operator+(const T & lhs, const uint128_t & rhs){
    return rhs + lhs;
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator+=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (rhs + lhs);
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
uint128_t operator-(const T & lhs, const uint128_t & rhs){
    return -(rhs - lhs);
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator-=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (-(rhs - lhs));
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
uint128_t operator*(const T & lhs, const uint128_t & rhs){
    return rhs * lhs;
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator*=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (rhs * lhs);
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
uint128_t operator/(const T & lhs, const uint128_t & rhs){
    return uint128_t(lhs) / rhs;
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator/=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (uint128_t(lhs) / rhs);
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
uint128_t operator%(const T & lhs, const uint128_t & rhs){
    return uint128_t(lhs) % rhs;
}

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T & operator%=(T & lhs, const uint128_t & rhs){
    return lhs = static_cast <T> (uint128_t(lhs) % rhs);
}

// IO Operator
UINT128_T_EXTERN std::ostream & operator<<(std::ostream & stream, const uint128_t & rhs);
#endif
```

#### **uint128\_t\_config.include**

```cpp
#ifndef _UINT128_T_CONFIG_
  #define _UINT128_T_CONFIG_
  #if defined(_MSC_VER)
    #if defined(_DLL)
      #define _UINT128_T_EXPORT __declspec(dllexport)
      #define _UINT128_T_IMPORT __declspec(dllimport)
    #else
      #define _UINT128_T_EXPORT
      #define _UINT128_T_IMPORT
    #endif
  #else
    // All modules on Unix are compiled with -fvisibility=hidden
    // All API symbols get visibility default
    // whether or not we're static linking or dynamic linking (with -fPIC)
    #define _UINT128_T_EXPORT __attribute__((visibility("default")))
    #define _UINT128_T_IMPORT __attribute__((visibility("default")))
  #endif
#endif


```

{% endtab %}

{% tab title="Go" %}

```go
package ipv6distreet

import (
	"errors"
	"io/ioutil"
	"log"
	"math/big"
	"net"
	"strconv"
	"strings"
	"sync"
)

type IpInfoV6 struct {
	prefStart map[uint32]uint32
	prefEnd   map[uint32]uint32
	data      []byte
	numbers   uint32
}

var objV6 *IpInfoV6
var onceV6 sync.Once

func GetDistrictObjectV6(filePath string) *IpInfoV6 {
	onceV6.Do(func() {
		objV6 = &IpInfoV6{}
		var err error
		objV6, err = LoadFileV6(filePath)
		if err != nil {
			log.Fatal("the IP Dat loaded failed!ipv6_district.dat")
		}
	})
	return objV6
}

func LoadFileV6(file string) (*IpInfoV6, error) {
	p := IpInfoV6{}
	var err error
	p.data, err = ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	p.numbers = unpackInt4byteV6(p.data[4], p.data[5], p.data[6], p.data[7])
	p.prefStart = make(map[uint32]uint32)
	p.prefEnd = make(map[uint32]uint32)
	for k := uint32(0); k < p.numbers; k++ {
		i := k*12 + 4 + 4
		p.prefStart[unpackInt4byteV6(p.data[i+8], p.data[i+9], p.data[i+10], p.data[i+11])] = unpackInt4byteV6(p.data[i], p.data[i+1], p.data[i+2], p.data[i+3])
		p.prefEnd[unpackInt4byteV6(p.data[i+8], p.data[i+9], p.data[i+10], p.data[i+11])] = unpackInt4byteV6(p.data[i+4], p.data[i+5], p.data[i+6], p.data[i+7])
	}

	return &p, err

}

func (p *IpInfoV6) GetV6(ip string) (string, error) {
	ips := strings.Split(ip, ":")

	parseUint, _ := strconv.ParseUint(ips[0], 16, 32)

	prefix := uint32(parseUint)

	intIP, err := ipToIntV6(ip)
	if err != nil {
		return "", err
	}

	low := p.prefStart[prefix]
	high := p.prefEnd[prefix]

	var cur uint32
	if low == high {
		cur = low
	} else {
		cur = p.SearchV6(low, high, intIP)
	}
	return p.getAddr(cur), nil

}

func (p *IpInfoV6) getAddr(cur uint32) string {
	j := p.numbers*12 + 4 + 4 + (cur * 55)
	offset := unpackInt4byteV6(p.data[j+50], p.data[1+j+50], p.data[2+j+50], p.data[3+j+50])
	length := uint32(p.data[50+j+4])
	return string(p.data[offset:int(offset+length)])
}

func (p *IpInfoV6) SearchV6(low uint32, high uint32, k *big.Int) uint32 {
	var M uint32 = 0
	for low <= high {
		mid := (low + high) / 2
		j := p.numbers*12 + 4 + 4 + (mid * 55)
		endipnum := new(big.Int)
		endipnumInt, _ := endipnum.SetString(strings.ReplaceAll(string(p.data[j:j+50]), "*", ""), 10)

		if endipnumInt.Cmp(k) == 0 || endipnumInt.Cmp(k) == 1 {
			M = mid
			if mid == 0 {
				break
			}
			high = mid - 1
		} else {
			low = mid + 1
		}
	}

	return M
}

func ipToIntV6(ipstr string) (*big.Int, error) {
	ip := net.ParseIP(ipstr)
	ip = ip.To16()

	if ip == nil {
		return big.NewInt(0), errors.New("invalid ipv6")
	}
	IPv6Int := big.NewInt(0)
	IPv6Int.SetBytes(ip)
	return IPv6Int, nil
}

func unpackInt4byteV6(a, b, c, d byte) uint32 {
	return (uint32(a) & 0xFF) | ((uint32(b) << 8) & 0xFF00) | ((uint32(c) << 16) & 0xFF0000) | ((uint32(d) << 24) & 0xFF000000)
}
```

{% endtab %}

{% tab title="Lua" %}

#### **ipdata\_v6.lua**

```lua
ipdata = { prefStart = {}, prefEnd = {},v6_num = 0,
    endArr = {}, addrArr = {},
    file = {}, _version = "0.1.2",big_num = {'170141183460469231731687303715884105728', '85070591730234615865843651857942052864', '42535295865117307932921825928971026432', '21267647932558653966460912964485513216', '10633823966279326983230456482242756608', '5316911983139663491615228241121378304', '2658455991569831745807614120560689152', '1329227995784915872903807060280344576', '664613997892457936451903530140172288', '332306998946228968225951765070086144', '166153499473114484112975882535043072', '83076749736557242056487941267521536', '41538374868278621028243970633760768', '20769187434139310514121985316880384', '10384593717069655257060992658440192', '5192296858534827628530496329220096', '2596148429267413814265248164610048', '1298074214633706907132624082305024', '649037107316853453566312041152512', '324518553658426726783156020576256', '162259276829213363391578010288128', '81129638414606681695789005144064', '40564819207303340847894502572032', '20282409603651670423947251286016', '10141204801825835211973625643008', '5070602400912917605986812821504', '2535301200456458802993406410752', '1267650600228229401496703205376', '633825300114114700748351602688', '316912650057057350374175801344', '158456325028528675187087900672', '79228162514264337593543950336', '39614081257132168796771975168', '19807040628566084398385987584', '9903520314283042199192993792', '4951760157141521099596496896', '2475880078570760549798248448', '1237940039285380274899124224', '618970019642690137449562112', '309485009821345068724781056', '154742504910672534362390528', '77371252455336267181195264', '38685626227668133590597632', '19342813113834066795298816', '9671406556917033397649408', '4835703278458516698824704', '2417851639229258349412352', '1208925819614629174706176', '604462909807314587353088', '302231454903657293676544', '151115727451828646838272', '75557863725914323419136', '37778931862957161709568', '18889465931478580854784', '9444732965739290427392', '4722366482869645213696', '2361183241434822606848', '1180591620717411303424', '590295810358705651712', '295147905179352825856', '147573952589676412928', '73786976294838206464', '36893488147419103232', '18446744073709551616', '9223372036854775808', '4611686018427387904', '2305843009213693952', '1152921504606846976', '576460752303423488', '288230376151711744', '144115188075855872', '72057594037927936', '36028797018963968', '18014398509481984', '9007199254740992', '4503599627370496', '2251799813685248', '1125899906842624', '562949953421312', '281474976710656', '140737488355328', '70368744177664', '35184372088832', '17592186044416', '8796093022208', '4398046511104', '2199023255552', '1099511627776', '549755813888', '274877906944', '137438953472', '68719476736', '34359738368', '17179869184', '8589934592', '4294967296', '2147483648', '1073741824', '536870912', '268435456', '134217728', '67108864', '33554432', '16777216', '8388608', '4194304', '2097152', '1048576', '524288', '262144', '131072', '65536', '32768', '16384', '8192', '4096', '2048', '1024', '512', '256', '128', '64', '32', '16', '8', '4', '2', '1'},
    offset_data = nil
}

function unpackInt4byte(a, b, c, d)
    return (a % 256) + (b * 256) + (c * 65536) + (d * 16777216)
end

function fsize(file)
    local current = file:seek()
    local size = file:seek("end")
    file:seek("set", current)
    return size
end

function redaFileBytes(file, length, off)
    file:seek("set", off)
    local str = file:read(length)
    local bytes = {}
    for i = 1, #str do
        table.insert(bytes, string.byte(str, i))
    end
    return bytes
end

function ipdata.LoadFile(path)
    local sizeData = {}

    ipdata.file = io.open(path, "rb")
    local num_data = redaFileBytes(ipdata.file, 4, 4)
    ipdata.v6_num = unpackInt4byte(num_data[1], num_data[2], num_data[3], num_data[4])
    sizeData = redaFileBytes(ipdata.file, ipdata.v6_num*12, 8)

    for k = 1, ipdata.v6_num do
        local i = (k-1) * 12
        local preoff = unpackInt4byte(sizeData[i+9], sizeData[i+10], sizeData[i+11], sizeData[i+12])
        ipdata.prefStart[preoff] = unpackInt4byte(sizeData[i+1], sizeData[i+2], sizeData[i+3], sizeData[i+4])
        ipdata.prefEnd[preoff] = unpackInt4byte(sizeData[i+5], sizeData[i+6], sizeData[i+7], sizeData[i+8])
    end

    sizeData = redaFileBytes(ipdata.file, 4, 0)
    local recordSize = unpackInt4byte(sizeData[1], sizeData[2], sizeData[3], sizeData[4])
    local p = ipdata.v6_num*12+4+4
    ipdata.file:seek("set", p)
    ipdata.offset_data = ipdata.file:read(55*recordSize+1)
end

function ipdata.getAddr(row)
    local j = ipdata.v6_num*12+4+4 + (row * 55)
    ipdata.file:seek("set", j + 50)
    local tempIndexData = ipdata.file:read(5)
    local offset = unpackInt4byte(string.byte(tempIndexData, 1), string.byte(tempIndexData, 2), string.byte(tempIndexData, 3), string.byte(tempIndexData, 4))
    local length = string.byte(tempIndexData, 5)
    ipdata.file:seek("set", offset)
    local tempData = ipdata.file:read(length)
    return tempData
end

function ip2int(ip)
    local o1, o2, o3, o4 = string.match(ip, "(%d+)%.(%d+)%.(%d+)%.(%d+)")
    local num = 2 ^ 24 * o1 + 2 ^ 16 * o2 + 2 ^ 8 * o3 + o4
    return math.floor(num)
end

function ipdata.Search(low, high, k)
    local M = 0
    while low <= high do
        local mid = math.floor((low + high) / 2)
        local offaa = (mid)*55+1
        local data_len = (mid)*55+50
        local endipNum = string.gsub(string.sub(ipdata.offset_data,offaa,data_len), "*", "") 

        if endipNum == nil then
            break
        end
        if endipNum >= k then
            M = mid
            if mid == 0 then
                break
            end
            high = mid - 1
        else
            low = mid + 1
        end
    end
    return M
end

function ipdata.FindIP(ip)
    local ip_str = tranSimIpv6ToFullIpv6(ip)
    local prefix = tonumber(string.sub(ip_str,1,4), 16)
    local low = ipdata.prefStart[prefix]
    local high = ipdata.prefEnd[prefix]
    local ip_2 = ipv6bin(ip_str)


    local intIP = "0"
    for i = 1, string.len(ip_2) do
        if string.sub(ip_2,i,i) == '1' then
            local num1 = ipdata.big_num[i]
            if string.len(num1)>string.len(intIP) then
                intIP = bigNumberAdd(num1, intIP)
            else
                intIP = bigNumberAdd(intIP, num1)
            end
        end
    end

    local cur = 0
    if low == high then
        cur = low
    else
        cur = ipdata.Search(low, high, intIP)
    end

    if cur == 100000000 then
        return "0.0.0.0"
    else
        return ipdata.getAddr(cur)
    end
end

function bigNumberAdd(a, b)  
    local result = "" 
    local carry = 0 
    local lenA = #a  
    local lenB = #b  
    local i = 1  
  
    if lenA < lenB then  
        a, b = b, a  
        lenA, lenB = lenB, lenA  
    end  
  
    while i <= lenB do  
        local sum = tonumber(a:sub(lenA - i + 1, lenA - i + 1)) + tonumber(b:sub(lenB - i + 1, lenB - i + 1)) + carry  
        result = tostring(sum % 10) .. result
        carry = math.floor(sum / 10)
        i = i + 1  
    end  
  
    while i <= lenA do  
        local sum = tonumber(a:sub(lenA - i + 1, lenA - i + 1)) + carry  
        result = tostring(sum % 10) .. result  
        carry = math.floor(sum / 10)  
        i = i + 1  
    end  
  
    if carry > 0 then  
        result = tostring(carry) .. result  
    end  
  
    return result  
end  


function ipv6bin(ipstr)
    ipstr = tranSimIpv6ToFullIpv6(ipstr)
    ipstr = ipstr:gsub(":", "")
    local ipbin = ""
    for i = 1, 32, 2 do
        local str_16 = tonumber(string.sub(ipstr,i,i+1), 16)
        local bin = ""
        while str_16>0 do
            bin = tostring(str_16 % 2)..bin
            str_16 = math.floor(str_16/2)
        end
        bin = string.sub("00000000"..bin,-8)
        ipbin = ipbin..bin
    end
    return ipbin
end

function tranSimIpv6ToFullIpv6(simpeIpv6)  
    simpeIpv6 = string.upper(simpeIpv6)
  
    if simpeIpv6 == "::" then  
        return "0000:0000:0000:0000:0000:0000:0000:0000"  
    end  
  
    local arr = {"0000", "0000", "0000", "0000", "0000", "0000", "0000", "0000"}
    if string.sub(simpeIpv6, 1, 2) == "::" then  
        local tmpArr = {}  
        for part in string.gmatch(string.sub(simpeIpv6, 3), "%S+") do  
            table.insert(tmpArr, string.sub("0000"..part,-4))  
        end  
        for i = 1, #tmpArr do  
            arr[i + 8 - #tmpArr] = tmpArr[i]  
        end  
    elseif string.sub(simpeIpv6, -2) == "::" then  
        local tmpArr = {}  
        for part in string.gmatch(string.sub(simpeIpv6, 1, -3), "%S+") do  
            local aaa = string_split(part, ":")
            for j=1,#aaa do
                table.insert(tmpArr, string.sub("0000"..aaa[j],-4))  
            end 
        end  
        for i = 1, #tmpArr do  
            arr[i] = tmpArr[i]  
        end  
    elseif string.find(simpeIpv6, "::") then  
        local tmpArr = string_split(simpeIpv6, "::")  
        local tmpArr0 = string_split(tmpArr[1], ":")  
        for i = 1, #tmpArr0 do  
            arr[i] = string.sub("0000"..tmpArr0[i],-4)
        end  
        local tmpArr1 = string_split(tmpArr[2], ":")  
        for i = 1, #tmpArr1 do  
            arr[i + 8 - #tmpArr1] = string.sub("0000"..tmpArr1[i],-4)
        end  
    else  
        local tmpArr = string_split(simpeIpv6, ":")  
        for i = 1, #tmpArr do  
            arr[i + 8 - #tmpArr] = string.sub("0000"..tmpArr[i],-4)
        end  
    end  
    return table.concat(arr, ":")  
end  

function string_split(str, delimiter)  
    local result = {}  
    for match in (str..delimiter):gmatch("(.-)"..delimiter) do  
        table.insert(result, match)  
    end  
    return result  
end  

return ipdata
```

#### **search\_v6.lua**

```lua
local search = require "ipdata_v6"
search.LoadFile("ipv6_city.dat")

while true do
    print("Please enter content:")
    local input = io.read()
    local findIp = search.FindIP(input)
    print(findIp)
end
```

{% endtab %}

{% tab title="Rust" %}

```rust
use std::collections::HashMap;
use std::fs;
use std::error::Error;
use std::net::Ipv6Addr;
use std::str::FromStr;
use std::time::Instant;


struct IpInfoV6 {
    pref_start: HashMap<u32, u32>,
    pref_end: HashMap<u32, u32>,
    data:Vec<u8>,
    numbers:usize,
}

impl IpInfoV6 {
    fn get_district_object_v6() -> Result<Self, Box<dyn Error>> {
        let mut p = IpInfoV6 {
            pref_start: HashMap::new(),
            pref_end: HashMap::new(),
            data:Vec::new(),
            numbers:0,
        };

        p.data = fs::read("ipv6_district.dat")?;
        p.numbers = unpack_int_4byte_v6(p.data[4], p.data[5], p.data[6], p.data[7]) as usize;
        let mut i = 0;
        while i < p.numbers {
            let k = i * 12 + 4 + 4;
            let start = unpack_int_4byte_v6(p.data[k + 8], p.data[k + 9], p.data[k + 10], p.data[k + 11]);
            let end = unpack_int_4byte_v6(p.data[k], p.data[k + 1], p.data[k + 2], p.data[k + 3]);
            p.pref_start.insert(start, end);
            let end = unpack_int_4byte_v6(p.data[k + 4], p.data[k + 5], p.data[k + 6], p.data[k + 7]);
            p.pref_end.insert(start, end);
            i += 1;
        }

        Ok(p)
    }

    fn get_v6(&self, ip: &str) -> Result<String, Box<dyn Error>> {
        let ips: Vec<&str> = ip.split(":").collect();
        let prefix = u32::from_str_radix(ips[0], 16)?;
        let int_ip = ip_to_int_v6(ip)?;
        let low = *self.pref_start.get(&prefix).ok_or("Prefix not found")?;
        let high = *self.pref_end.get(&prefix).ok_or("Prefix not found")?;
        let cur = if low == high {
            low
        } else {
            self.search_v6(low, high, &int_ip)
        };
        let addr = self.get_addr(cur as usize)?;
        Ok(addr)
    }

    fn get_addr(&self,cur:usize)-> Result<String, Box<dyn Error>>{
        let j = self.numbers * 12 + 4 + 4 + (cur * 55);
        let offset = unpack_int_4byte_v6(self.data[j + 50], self.data[1 + j + 50], self.data[2 + j + 50], self.data[3 + j + 50]) as usize;
        let length = self.data[50 + j + 4] as usize;
        Ok(String::from_utf8_lossy(&self.data[offset..offset + length]).to_string())
    }

    fn search_v6(&self, mut low: u32, mut high: u32, k: &u128) -> u32 {
        let mut m = 0;
        while low <= high {
            let mid = (low + high) / 2;
            let j = self.numbers * 12 + 4 + 4 + (mid as usize * 55);

            let result = u128::from_str(&String::from_utf8_lossy(&self.data[j..j + 50]).replace("*", ""));
            let end_ip_num = result.unwrap();
            if end_ip_num >= *k {
                m = mid;
                if mid == 0 {
                    break;
                }
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        m
    }
}



fn ip_to_int_v6(ip_str: &str) -> Result<u128, Box<dyn Error>> {
    let ip = Ipv6Addr::from_str(ip_str)?;
    Ok(u128::from(ip))
}

fn unpack_int_4byte_v6(a: u8, b: u8, c: u8, d: u8) -> u32 {
    u32::from(a) | (u32::from(b) << 8) | (u32::from(c) << 16) | (u32::from(d) << 24)
}


fn main() {
    let start_time = Instant::now();
    let ip_info = IpInfoV6::get_district_object_v6().unwrap();
   
    let district = ip_info.get_v6("2001:250:2826::").unwrap();
    println!("{}", district);

    let end_time = Instant::now();
    let elapsed_time = end_time - start_time;
    println!("Function took {} millisecond to execute.", elapsed_time.as_millis());
}



```

{% endtab %}
{% endtabs %}


