├── .github └── workflows │ └── static-qt-go.yml ├── .gitignore ├── Ip2regionTool-qt ├── Ip2regionTool-qt.pro ├── favicon.ico ├── ip2region.cpp ├── ip2region.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui └── version.rc ├── LICENSE ├── README.md ├── cmd └── main.go ├── export └── main.go ├── go.mod ├── go.sum ├── image ├── 1.png ├── 2.png └── 3.png ├── tool.go ├── xdb.go ├── xdb_index.go ├── xdb_maker.go ├── xdb_segment.go └── xdb_util.go /.github/workflows/static-qt-go.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | release: 8 | types: [ created ] 9 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 10 | jobs: 11 | # This workflow contains a single job called "build" 12 | build: 13 | # The type of runner that the job will run on 14 | runs-on: windows-latest 15 | 16 | # Steps represent a sequence of tasks that will be executed as part of the job 17 | steps: 18 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 19 | - uses: actions/checkout@v3 20 | - name: install qt static 21 | uses: orestonce/install-qt-static@v0.4.0 22 | with: 23 | version: Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104 24 | - name: Setup Go environment 25 | uses: actions/setup-go@v3.2.1 26 | with: 27 | # The Go version to download (if necessary) and use. Supports semver spec and ranges. 28 | go-version: 1.12.17 # optional 29 | - name: build binary 30 | run: | 31 | go mod tidy 32 | go run export/main.go 33 | cd Ip2regionTool-qt && qmake && mingw32-make release && cd .. 34 | dir Ip2regionTool-qt\release\Ip2regionTool-qt.exe 35 | - name: upload release windows_386_qt 36 | uses: Guerra24/upload-to-release@v1 37 | with: 38 | name: Ip2regionTool_qt_windows_386-${{ github.ref_name }}.exe 39 | path: Ip2regionTool-qt\release\Ip2regionTool-qt.exe 40 | repo-token: ${{ secrets.GITHUB_TOKEN }} 41 | - name: upload release windows_386_cli 42 | uses: Guerra24/upload-to-release@v1 43 | with: 44 | name: Ip2regionTool_cli_windows_386-${{ github.ref_name }}.exe 45 | path: bin/Ip2regionTool_cli_windows_386.exe 46 | repo-token: ${{ secrets.GITHUB_TOKEN }} 47 | - name: upload release linux_386_cli 48 | uses: Guerra24/upload-to-release@v1 49 | with: 50 | name: Ip2regionTool_cli_linux_386-${{ github.ref_name }} 51 | path: bin/Ip2regionTool_cli_linux_386 52 | repo-token: ${{ secrets.GITHUB_TOKEN }} 53 | - name: upload release darwin_amd64_cli 54 | uses: Guerra24/upload-to-release@v1 55 | with: 56 | name: Ip2regionTool_cli_darwin_amd64-${{ github.ref_name }} 57 | path: bin/Ip2regionTool_cli_darwin_amd64 58 | repo-token: ${{ secrets.GITHUB_TOKEN }} 59 | 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /Ip2regionTool-qt/Ip2regionTool-qt.pro.user 3 | /Ip2regionTool-qt/ip2region-impl.a 4 | /Ip2regionTool-qt/ip2region-impl.go 5 | -------------------------------------------------------------------------------- /Ip2regionTool-qt/Ip2regionTool-qt.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2021-11-07T09:56:42 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = Ip2regionTool-qt 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which as been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | 26 | SOURCES += main.cpp\ 27 | mainwindow.cpp \ 28 | ip2region.cpp 29 | 30 | HEADERS += mainwindow.h \ 31 | ip2region.h 32 | 33 | FORMS += mainwindow.ui 34 | 35 | RC_FILE += version.rc 36 | 37 | LIBS += -L$$PWD -lip2region-impl 38 | 39 | QMAKE_CXXFLAGS += -std=c++11 40 | -------------------------------------------------------------------------------- /Ip2regionTool-qt/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orestonce/Ip2regionTool/df7b9be1cdfacaab7cc7e1fe5569f8aeba005304/Ip2regionTool-qt/favicon.ico -------------------------------------------------------------------------------- /Ip2regionTool-qt/ip2region.cpp: -------------------------------------------------------------------------------- 1 | #include "ip2region.h" 2 | /* Code generated by cmd/cgo; DO NOT EDIT. */ 3 | 4 | /* package command-line-arguments */ 5 | 6 | 7 | #line 1 "cgo-builtin-export-prolog" 8 | 9 | #include 10 | 11 | #ifndef GO_CGO_EXPORT_PROLOGUE_H 12 | #define GO_CGO_EXPORT_PROLOGUE_H 13 | 14 | #ifndef GO_CGO_GOSTRING_TYPEDEF 15 | typedef struct { const char *p; ptrdiff_t n; } _GoString_; 16 | #endif 17 | 18 | #endif 19 | 20 | /* Start of preamble from import "C" comments. */ 21 | 22 | 23 | 24 | 25 | /* End of preamble from import "C" comments. */ 26 | 27 | 28 | /* Start of boilerplate cgo prologue. */ 29 | #line 1 "cgo-gcc-export-header-prolog" 30 | 31 | #ifndef GO_CGO_PROLOGUE_H 32 | #define GO_CGO_PROLOGUE_H 33 | 34 | typedef signed char GoInt8; 35 | typedef unsigned char GoUint8; 36 | typedef short GoInt16; 37 | typedef unsigned short GoUint16; 38 | typedef int GoInt32; 39 | typedef unsigned int GoUint32; 40 | typedef long long GoInt64; 41 | typedef unsigned long long GoUint64; 42 | typedef GoInt64 GoInt; 43 | typedef GoUint64 GoUint; 44 | typedef size_t GoUintptr; 45 | typedef float GoFloat32; 46 | typedef double GoFloat64; 47 | #ifdef _MSC_VER 48 | #include 49 | typedef _Fcomplex GoComplex64; 50 | typedef _Dcomplex GoComplex128; 51 | #else 52 | typedef float _Complex GoComplex64; 53 | typedef double _Complex GoComplex128; 54 | #endif 55 | 56 | /* 57 | static assertion to make sure the file is being used on architecture 58 | at least with matching size of GoInt. 59 | */ 60 | typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; 61 | 62 | #ifndef GO_CGO_GOSTRING_TYPEDEF 63 | typedef _GoString_ GoString; 64 | #endif 65 | typedef void *GoMap; 66 | typedef void *GoChan; 67 | typedef struct { void *t; void *v; } GoInterface; 68 | typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; 69 | 70 | #endif 71 | 72 | /* End of boilerplate cgo prologue. */ 73 | 74 | #ifdef __cplusplus 75 | extern "C" { 76 | #endif 77 | 78 | extern __declspec(dllexport) void Go2cppFn_ConvertDbToTxt(char* in, int inLen, char** out, int* outLen); 79 | extern __declspec(dllexport) void Go2cppFn_GetDbVersionByName(char* in, int inLen, char** out, int* outLen); 80 | extern __declspec(dllexport) void Go2cppFn_ConvertTxtToDb(char* in, int inLen, char** out, int* outLen); 81 | extern __declspec(dllexport) void Go2cppFn_TxtToXdb(char* in, int inLen, char** out, int* outLen); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | 88 | std::string ConvertDbToTxt(ConvertDbToTxt_Req in0){ 89 | std::string in; 90 | { 91 | { 92 | uint32_t tmp10 = in0.DbFileName.length(); 93 | char tmp11[4]; 94 | tmp11[0] = (uint32_t(tmp10) >> 24) & 0xFF; 95 | tmp11[1] = (uint32_t(tmp10) >> 16) & 0xFF; 96 | tmp11[2] = (uint32_t(tmp10) >> 8) & 0xFF; 97 | tmp11[3] = (uint32_t(tmp10) >> 0) & 0xFF; 98 | in.append(tmp11, 4); 99 | in.append(in0.DbFileName); 100 | } 101 | { 102 | uint32_t tmp12 = in0.TxtFileName.length(); 103 | char tmp13[4]; 104 | tmp13[0] = (uint32_t(tmp12) >> 24) & 0xFF; 105 | tmp13[1] = (uint32_t(tmp12) >> 16) & 0xFF; 106 | tmp13[2] = (uint32_t(tmp12) >> 8) & 0xFF; 107 | tmp13[3] = (uint32_t(tmp12) >> 0) & 0xFF; 108 | in.append(tmp13, 4); 109 | in.append(in0.TxtFileName); 110 | } 111 | in.append((char*)(&in0.Merge), 1); 112 | { 113 | char tmp14[4]; 114 | tmp14[0] = (uint32_t(in0.DbVersion) >> 24) & 0xFF; 115 | tmp14[1] = (uint32_t(in0.DbVersion) >> 16) & 0xFF; 116 | tmp14[2] = (uint32_t(in0.DbVersion) >> 8) & 0xFF; 117 | tmp14[3] = (uint32_t(in0.DbVersion) >> 0) & 0xFF; 118 | in.append(tmp14, 4); 119 | } 120 | } 121 | char *out = NULL; 122 | int outLen = 0; 123 | Go2cppFn_ConvertDbToTxt((char *)in.data(), in.length(), &out, &outLen); 124 | std::string retValue; 125 | int outIdx = 0; 126 | { 127 | uint32_t tmp15 = 0; 128 | uint32_t tmp16 = uint32_t(uint8_t(out[outIdx+0]) << 24); 129 | uint32_t tmp17 = uint32_t(uint8_t(out[outIdx+1]) << 16); 130 | uint32_t tmp18 = uint32_t(uint8_t(out[outIdx+2]) << 8); 131 | uint32_t tmp19 = uint32_t(uint8_t(out[outIdx+3]) << 0); 132 | tmp15 = tmp16 | tmp17 | tmp18 | tmp19; 133 | outIdx+=4; 134 | retValue = std::string(out+outIdx, out+outIdx+tmp15); 135 | outIdx+=tmp15; 136 | } 137 | if (out != NULL) { 138 | free(out); 139 | } 140 | return retValue; 141 | } 142 | 143 | int32_t GetDbVersionByName(std::string in0){ 144 | std::string in; 145 | { 146 | uint32_t tmp5 = in0.length(); 147 | char tmp6[4]; 148 | tmp6[0] = (uint32_t(tmp5) >> 24) & 0xFF; 149 | tmp6[1] = (uint32_t(tmp5) >> 16) & 0xFF; 150 | tmp6[2] = (uint32_t(tmp5) >> 8) & 0xFF; 151 | tmp6[3] = (uint32_t(tmp5) >> 0) & 0xFF; 152 | in.append(tmp6, 4); 153 | in.append(in0); 154 | } 155 | char *out = NULL; 156 | int outLen = 0; 157 | Go2cppFn_GetDbVersionByName((char *)in.data(), in.length(), &out, &outLen); 158 | int32_t retValue; 159 | int outIdx = 0; 160 | { 161 | uint32_t tmp7 = uint32_t(uint8_t(out[outIdx+0]) << 24); 162 | uint32_t tmp8 = uint32_t(uint8_t(out[outIdx+1]) << 16); 163 | uint32_t tmp9 = uint32_t(uint8_t(out[outIdx+2]) << 8); 164 | uint32_t tmp10 = uint32_t(uint8_t(out[outIdx+3]) << 0); 165 | retValue = tmp7 | tmp8 | tmp9 | tmp10; 166 | outIdx+=4; 167 | } 168 | if (out != NULL) { 169 | free(out); 170 | } 171 | return retValue; 172 | } 173 | 174 | std::string ConvertTxtToDb(ConvertTxtToDb_Req in0){ 175 | std::string in; 176 | { 177 | { 178 | uint32_t tmp12 = in0.TxtFileName.length(); 179 | char tmp13[4]; 180 | tmp13[0] = (uint32_t(tmp12) >> 24) & 0xFF; 181 | tmp13[1] = (uint32_t(tmp12) >> 16) & 0xFF; 182 | tmp13[2] = (uint32_t(tmp12) >> 8) & 0xFF; 183 | tmp13[3] = (uint32_t(tmp12) >> 0) & 0xFF; 184 | in.append(tmp13, 4); 185 | in.append(in0.TxtFileName); 186 | } 187 | { 188 | uint32_t tmp14 = in0.DbFileName.length(); 189 | char tmp15[4]; 190 | tmp15[0] = (uint32_t(tmp14) >> 24) & 0xFF; 191 | tmp15[1] = (uint32_t(tmp14) >> 16) & 0xFF; 192 | tmp15[2] = (uint32_t(tmp14) >> 8) & 0xFF; 193 | tmp15[3] = (uint32_t(tmp14) >> 0) & 0xFF; 194 | in.append(tmp15, 4); 195 | in.append(in0.DbFileName); 196 | } 197 | { 198 | uint32_t tmp16 = in0.RegionCsvFileName.length(); 199 | char tmp17[4]; 200 | tmp17[0] = (uint32_t(tmp16) >> 24) & 0xFF; 201 | tmp17[1] = (uint32_t(tmp16) >> 16) & 0xFF; 202 | tmp17[2] = (uint32_t(tmp16) >> 8) & 0xFF; 203 | tmp17[3] = (uint32_t(tmp16) >> 0) & 0xFF; 204 | in.append(tmp17, 4); 205 | in.append(in0.RegionCsvFileName); 206 | } 207 | in.append((char*)(&in0.Merge), 1); 208 | } 209 | char *out = NULL; 210 | int outLen = 0; 211 | Go2cppFn_ConvertTxtToDb((char *)in.data(), in.length(), &out, &outLen); 212 | std::string retValue; 213 | int outIdx = 0; 214 | { 215 | uint32_t tmp18 = 0; 216 | uint32_t tmp19 = uint32_t(uint8_t(out[outIdx+0]) << 24); 217 | uint32_t tmp20 = uint32_t(uint8_t(out[outIdx+1]) << 16); 218 | uint32_t tmp21 = uint32_t(uint8_t(out[outIdx+2]) << 8); 219 | uint32_t tmp22 = uint32_t(uint8_t(out[outIdx+3]) << 0); 220 | tmp18 = tmp19 | tmp20 | tmp21 | tmp22; 221 | outIdx+=4; 222 | retValue = std::string(out+outIdx, out+outIdx+tmp18); 223 | outIdx+=tmp18; 224 | } 225 | if (out != NULL) { 226 | free(out); 227 | } 228 | return retValue; 229 | } 230 | 231 | std::string TxtToXdb(TxtToXdbReq in0){ 232 | std::string in; 233 | { 234 | { 235 | uint32_t tmp11 = in0.SrcFile.length(); 236 | char tmp12[4]; 237 | tmp12[0] = (uint32_t(tmp11) >> 24) & 0xFF; 238 | tmp12[1] = (uint32_t(tmp11) >> 16) & 0xFF; 239 | tmp12[2] = (uint32_t(tmp11) >> 8) & 0xFF; 240 | tmp12[3] = (uint32_t(tmp11) >> 0) & 0xFF; 241 | in.append(tmp12, 4); 242 | in.append(in0.SrcFile); 243 | } 244 | { 245 | uint32_t tmp13 = in0.DstFile.length(); 246 | char tmp14[4]; 247 | tmp14[0] = (uint32_t(tmp13) >> 24) & 0xFF; 248 | tmp14[1] = (uint32_t(tmp13) >> 16) & 0xFF; 249 | tmp14[2] = (uint32_t(tmp13) >> 8) & 0xFF; 250 | tmp14[3] = (uint32_t(tmp13) >> 0) & 0xFF; 251 | in.append(tmp14, 4); 252 | in.append(in0.DstFile); 253 | } 254 | { 255 | uint32_t tmp15 = in0.IndexPolicyS.length(); 256 | char tmp16[4]; 257 | tmp16[0] = (uint32_t(tmp15) >> 24) & 0xFF; 258 | tmp16[1] = (uint32_t(tmp15) >> 16) & 0xFF; 259 | tmp16[2] = (uint32_t(tmp15) >> 8) & 0xFF; 260 | tmp16[3] = (uint32_t(tmp15) >> 0) & 0xFF; 261 | in.append(tmp16, 4); 262 | in.append(in0.IndexPolicyS); 263 | } 264 | } 265 | char *out = NULL; 266 | int outLen = 0; 267 | Go2cppFn_TxtToXdb((char *)in.data(), in.length(), &out, &outLen); 268 | std::string retValue; 269 | int outIdx = 0; 270 | { 271 | uint32_t tmp17 = 0; 272 | uint32_t tmp18 = uint32_t(uint8_t(out[outIdx+0]) << 24); 273 | uint32_t tmp19 = uint32_t(uint8_t(out[outIdx+1]) << 16); 274 | uint32_t tmp20 = uint32_t(uint8_t(out[outIdx+2]) << 8); 275 | uint32_t tmp21 = uint32_t(uint8_t(out[outIdx+3]) << 0); 276 | tmp17 = tmp18 | tmp19 | tmp20 | tmp21; 277 | outIdx+=4; 278 | retValue = std::string(out+outIdx, out+outIdx+tmp17); 279 | outIdx+=tmp17; 280 | } 281 | if (out != NULL) { 282 | free(out); 283 | } 284 | return retValue; 285 | } 286 | 287 | 288 | 289 | // Qt: 290 | #include 291 | #include 292 | 293 | RunOnUiThread::RunOnUiThread(QObject *parent) : QObject(parent), m_done(false) 294 | { 295 | // 用signal里的Qt::QueuedConnection 将多线程里面的函数转化到ui线程里调用 296 | connect(this, SIGNAL(signal_newFn()), this, SLOT(slot_newFn()), Qt::QueuedConnection); 297 | } 298 | 299 | RunOnUiThread::~RunOnUiThread() 300 | { 301 | { 302 | QMutexLocker lk(&this->m_Mutex); 303 | this->m_done = true; 304 | this->m_funcList.clear(); 305 | } 306 | this->m_pool.clear(); 307 | this->m_pool.waitForDone(); 308 | } 309 | 310 | void RunOnUiThread::AddRunFnOn_OtherThread(std::function fn) 311 | { 312 | QMutexLocker lk(&this->m_Mutex); 313 | if (this->m_done) { 314 | return; 315 | } 316 | QtConcurrent::run(&this->m_pool, fn); 317 | } 318 | 319 | void RunOnUiThread::slot_newFn() 320 | { 321 | QVector> fn_vector; 322 | { 323 | QMutexLocker lk(&this->m_Mutex); 324 | if (this->m_funcList.empty() || this->m_done) { 325 | return; 326 | } 327 | fn_vector.swap(this->m_funcList); 328 | } 329 | 330 | for(std::function& fn : fn_vector) 331 | { 332 | bool v_done = false; 333 | { 334 | QMutexLocker lk(&this->m_Mutex); 335 | v_done = this->m_done; 336 | } 337 | if (v_done) { // 快速结束 338 | return; 339 | } 340 | fn(); 341 | } 342 | } 343 | 344 | void RunOnUiThread::AddRunFnOn_UiThread(std::function fn) 345 | { 346 | { 347 | QMutexLocker lk(&this->m_Mutex); 348 | if (this->m_done) { 349 | return; 350 | } 351 | m_funcList.push_back(fn); 352 | } 353 | emit this->signal_newFn(); 354 | } 355 | 356 | bool RunOnUiThread::Get_Done() 357 | { 358 | QMutexLocker lk(&this->m_Mutex); 359 | return this->m_done; 360 | } 361 | 362 | #include 363 | #include 364 | #include 365 | #include 366 | #include 367 | #include 368 | #include 369 | #include 370 | #include 371 | 372 | QString StringToRGBA(const QString &color); 373 | 374 | Toast::Toast(QObject *parent) : QObject(parent) 375 | { 376 | m_myWidget = new QWidget; 377 | m_myWidget->setFixedHeight(60); 378 | m_label = new QLabel; 379 | m_label->setFixedHeight(30); 380 | m_label->move(0,0); 381 | QFont ft; 382 | ft.setPointSize(10); 383 | m_label->setFont(ft); 384 | m_label->setAlignment(Qt::AlignCenter); 385 | m_label->setStyleSheet("color:white"); 386 | m_myWidget->setStyleSheet("border: none;background-color:black;border-radius:10px"); 387 | QHBoxLayout * la = new QHBoxLayout; 388 | la->addWidget(m_label); 389 | la->setContentsMargins(0,0,0,0); 390 | m_myWidget->setLayout(la); 391 | m_myWidget->hide(); 392 | m_myWidget->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint); 393 | m_myWidget->setAttribute(Qt::WA_TranslucentBackground,true); 394 | m_timer = new QTimer(); 395 | m_timer->setInterval(3000); 396 | connect(m_timer,SIGNAL(timeout()),this,SLOT(onTimerStayOut())); 397 | } 398 | 399 | Toast *Toast::Instance() 400 | { 401 | static Toast instance; 402 | return &instance; 403 | } 404 | 405 | void Toast::setText(const QString &color,const QString &bgcolor,const int & mestime,const QString &text){ 406 | QApplication::beep(); 407 | QFontMetrics fm(m_label->font()); 408 | int width = fm.boundingRect(text).width() + 30; 409 | m_myWidget->setFixedWidth(width); 410 | m_label->setFixedWidth(width); 411 | m_label->setText(text); 412 | QString style = QString("color:").append(StringToRGBA(color)); 413 | m_label->setStyleSheet(style); 414 | 415 | m_myWidget->setStyleSheet(QString("border: none;border-radius:10px;") 416 | .append("background-color:").append(StringToRGBA(bgcolor))); 417 | 418 | QDesktopWidget *pDesk = QApplication::desktop(); 419 | m_myWidget->move((pDesk->width() - m_myWidget->width()) / 2, (pDesk->height() - m_myWidget->height()) / 2); 420 | m_myWidget->show(); 421 | m_timer->setInterval(mestime); 422 | m_timer->stop(); 423 | m_timer->start(); 424 | } 425 | 426 | void Toast::SetError(const QString &text,const int & mestime){ 427 | setText("FFFFFF","FF0000",mestime,text); 428 | } 429 | 430 | void Toast::SetSuccess(const QString &text,const int & mestime){ 431 | setText("000000","00FF00",mestime,text); 432 | } 433 | 434 | void Toast::SetWaring(const QString &text,const int & mestime){ 435 | setText("FF0000","FFFF00",mestime,text); 436 | } 437 | 438 | void Toast::SetTips(const QString &text,const int & mestime){ 439 | setText("FFFFFF","0080FF",mestime,text); 440 | } 441 | 442 | QString StringToRGBA(const QString &color){ 443 | int r = color.mid(0,2).toInt(nullptr,16); 444 | int g = color.mid(2,2).toInt(nullptr,16); 445 | int b = color.mid(4,2).toInt(nullptr,16); 446 | int a = color.length()>=8?color.mid(6,2).toInt(nullptr,16):QString("FF").toInt(nullptr,16); 447 | QString style = QString("rgba(").append(QString::number(r)).append(",") 448 | .append(QString::number(g)).append(",") 449 | .append(QString::number(b)).append(",") 450 | .append(QString::number(a)) 451 | .append(");"); 452 | return style; 453 | } 454 | 455 | void Toast::onTimerStayOut() 456 | { 457 | m_timer->stop(); 458 | m_myWidget->hide(); 459 | } 460 | -------------------------------------------------------------------------------- /Ip2regionTool-qt/ip2region.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | //Qt Creator 需要在xxx.pro 内部增加静态库的链接声明 9 | //LIBS += -L$$PWD -lip2region-impl 10 | 11 | struct ConvertDbToTxt_Req{ 12 | std::string DbFileName; 13 | std::string TxtFileName; 14 | bool Merge; 15 | int32_t DbVersion; 16 | ConvertDbToTxt_Req(): Merge(false),DbVersion(0){} 17 | }; 18 | std::string ConvertDbToTxt(ConvertDbToTxt_Req in0); 19 | int32_t GetDbVersionByName(std::string in0); 20 | struct ConvertTxtToDb_Req{ 21 | std::string TxtFileName; 22 | std::string DbFileName; 23 | std::string RegionCsvFileName; 24 | bool Merge; 25 | ConvertTxtToDb_Req(): Merge(false){} 26 | }; 27 | std::string ConvertTxtToDb(ConvertTxtToDb_Req in0); 28 | struct TxtToXdbReq{ 29 | std::string SrcFile; 30 | std::string DstFile; 31 | std::string IndexPolicyS; 32 | }; 33 | std::string TxtToXdb(TxtToXdbReq in0); 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | class RunOnUiThread : public QObject 43 | { 44 | Q_OBJECT 45 | public: 46 | explicit RunOnUiThread(QObject *parent = nullptr); 47 | virtual ~RunOnUiThread(); 48 | 49 | void AddRunFnOn_OtherThread(std::function fn); 50 | // !!!注意,fn可能被调用,也可能由于RunOnUiThread被析构不被调用 51 | // 依赖于在fn里delete回收内存, 关闭文件等操作可能造成内存泄露 52 | void AddRunFnOn_UiThread(std::function fn); 53 | bool Get_Done(); 54 | signals: 55 | void signal_newFn(); 56 | private slots: 57 | void slot_newFn(); 58 | private: 59 | bool m_done; 60 | QVector> m_funcList; 61 | QMutex m_Mutex; 62 | QThreadPool m_pool; 63 | }; 64 | 65 | // Thanks: https://github.com/live-in-a-dream/Qt-Toast 66 | 67 | #include 68 | #include 69 | 70 | class QTimer; 71 | class QLabel; 72 | class QWidget; 73 | 74 | namespace Ui { 75 | class Toast; 76 | } 77 | 78 | class Toast : public QObject 79 | { 80 | Q_OBJECT 81 | 82 | public: 83 | explicit Toast(QObject *parent = nullptr); 84 | 85 | static Toast* Instance(); 86 | //错误 87 | void SetError(const QString &text,const int & mestime = 3000); 88 | //成功 89 | void SetSuccess(const QString &text,const int & mestime = 3000); 90 | //警告 91 | void SetWaring(const QString &text,const int & mestime = 3000); 92 | //提示 93 | void SetTips(const QString &text,const int & mestime = 3000); 94 | private slots: 95 | void onTimerStayOut(); 96 | private: 97 | void setText(const QString &color="FFFFFF",const QString &bgcolor = "000000",const int & mestime=3000,const QString &textconst=""); 98 | private: 99 | QWidget *m_myWidget; 100 | QLabel *m_label; 101 | QTimer *m_timer; 102 | Ui::Toast *ui; 103 | }; 104 | -------------------------------------------------------------------------------- /Ip2regionTool-qt/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /Ip2regionTool-qt/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include 4 | #include 5 | #include 6 | #include "ip2region.h" 7 | 8 | MainWindow::MainWindow(QWidget *parent) : 9 | QMainWindow(parent), 10 | ui(new Ui::MainWindow) 11 | { 12 | ui->setupUi(this); 13 | this->setTxtToXdb_IsRuning(false); 14 | } 15 | 16 | MainWindow::~MainWindow() 17 | { 18 | delete ui; 19 | } 20 | 21 | void MainWindow::on_pushButton_input_db_clicked() 22 | { 23 | QString input = QFileDialog::getOpenFileName(this, "", "", "*.db *.xdb"); 24 | if (!input.isEmpty()) { 25 | ui->lineEdit_input_db->setText(input); 26 | int version = GetDbVersionByName(input.toStdString()); 27 | if (version==1) { 28 | ui->radioButton_dbv1->setChecked(true); 29 | } else { 30 | ui->radioButton_dbv2->setChecked(true); 31 | } 32 | } 33 | } 34 | 35 | 36 | void MainWindow::on_pushButton_output_txt_clicked() 37 | { 38 | QString output = QFileDialog::getSaveFileName(this, "", "", "*.txt"); 39 | if (!output.isEmpty()) { 40 | ui->lineEdit_output_txt->setText(output); 41 | } 42 | } 43 | 44 | void MainWindow::on_pushButton_input_txt_clicked() 45 | { 46 | QString input = QFileDialog::getOpenFileName(this, "", "", "*.txt"); 47 | if (!input.isEmpty()) { 48 | ui->lineEdit_input_txt->setText(input); 49 | } 50 | } 51 | 52 | void MainWindow::on_pushButton_output_db_clicked() 53 | { 54 | QString output = QFileDialog::getSaveFileName(this, "", "", "*.db"); 55 | if (!output.isEmpty()) { 56 | ui->lineEdit_output_db->setText(output); 57 | } 58 | } 59 | 60 | void MainWindow::on_pushButton_input_regin_csv_clicked() 61 | { 62 | QString input = QFileDialog::getOpenFileName(this, "", "", "*.csv"); 63 | if (!input.isEmpty()) { 64 | ui->lineEdit_input_regin_csv->setText(input); 65 | } 66 | } 67 | 68 | void MainWindow::on_pushButton_DbToTxt_clicked() 69 | { 70 | QString db = ui->lineEdit_input_db->text(); 71 | QString txt = ui->lineEdit_output_txt->text(); 72 | if (db.isEmpty()||txt.isEmpty()) { 73 | return; 74 | } 75 | ConvertDbToTxt_Req req; 76 | if (ui->radioButton_dbv1->isChecked()) { 77 | req.DbVersion = 1; 78 | } else { 79 | req.DbVersion = 2; 80 | } 81 | req.DbFileName = db.toStdString(); 82 | req.TxtFileName = txt.toStdString(); 83 | req.Merge = ui->checkBox_DbToTxt_merge->isChecked(); 84 | std::string errMsg = ConvertDbToTxt(req); 85 | if (errMsg.empty()) { 86 | Toast::Instance()->SetSuccess("转换成功!"); 87 | return; 88 | } 89 | Toast::Instance()->SetError(QString::fromStdString(errMsg)); 90 | } 91 | 92 | void MainWindow::on_pushButton_TxtToDb_clicked() 93 | { 94 | QString txt = ui->lineEdit_input_txt->text(); 95 | QString db = ui->lineEdit_output_db->text(); 96 | if (db.isEmpty()||txt.isEmpty()) { 97 | return; 98 | } 99 | ConvertTxtToDb_Req req; 100 | req.TxtFileName = txt.toStdString(); 101 | req.DbFileName = db.toStdString(); 102 | req.RegionCsvFileName = ui->lineEdit_input_regin_csv->text().toStdString(); 103 | req.Merge = ui->checkBox_TxtToDb_merge->isChecked(); 104 | std::string errMsg = ConvertTxtToDb(req); 105 | if (errMsg.empty()) { 106 | Toast::Instance()->SetSuccess("转换成功!"); 107 | return; 108 | } 109 | Toast::Instance()->SetError(QString::fromStdString(errMsg)); 110 | } 111 | 112 | void MainWindow::on_tabWidget_currentChanged(int index) 113 | { 114 | ui->lineEdit_input_db->clear(); 115 | ui->lineEdit_input_txt->clear(); 116 | ui->lineEdit_output_db->clear(); 117 | ui->lineEdit_output_txt->clear(); 118 | ui->lineEdit_input_regin_csv->clear(); 119 | ui->checkBox_DbToTxt_merge->setChecked(false); 120 | ui->checkBox_TxtToDb_merge->setChecked(false); 121 | } 122 | 123 | 124 | void MainWindow::on_pushButton_xdb_srcFile_clicked() 125 | { 126 | QString input = QFileDialog::getOpenFileName(this, "", "", "*.txt"); 127 | if (!input.isEmpty()) { 128 | ui->lineEdit_xdb_srcFile->setText(input); 129 | } 130 | } 131 | 132 | void MainWindow::on_pushButton_xdb_dstFile_clicked() 133 | { 134 | QString output = QFileDialog::getSaveFileName(this, "", "", "*.xdb"); 135 | if (!output.isEmpty()) { 136 | ui->lineEdit_xdb_dstFile->setText(output); 137 | } 138 | } 139 | 140 | void MainWindow::on_pushButton_xdb_start_clicked() 141 | { 142 | TxtToXdbReq req; 143 | req.SrcFile = ui->lineEdit_xdb_srcFile->text().toStdString(); 144 | req.DstFile = ui->lineEdit_xdb_dstFile->text().toStdString(); 145 | if (req.SrcFile.empty() || req.DstFile.empty() || ui->lineEdit_xdb_srcFile->isEnabled() == false) { 146 | return; 147 | } 148 | this->setTxtToXdb_IsRuning(true); 149 | 150 | req.IndexPolicyS = ui->comboBox_xdb_indexPolicy->currentText().toStdString(); 151 | m_syncUi.AddRunFnOn_OtherThread([=](){ 152 | std::string errMsg = TxtToXdb(req); 153 | m_syncUi.AddRunFnOn_UiThread([=](){ 154 | this->setTxtToXdb_IsRuning(false); 155 | if (errMsg.empty()) { 156 | Toast::Instance()->SetSuccess("转换成功"); 157 | return; 158 | } 159 | Toast::Instance()->SetError("失败 " + QString::fromStdString(errMsg)); 160 | }); 161 | }); 162 | } 163 | 164 | void MainWindow::setTxtToXdb_IsRuning(bool runing) 165 | { 166 | ui->lineEdit_xdb_srcFile->setEnabled(runing == false); 167 | ui->lineEdit_xdb_dstFile->setEnabled(runing == false); 168 | ui->comboBox_xdb_indexPolicy->setEnabled(runing == false); 169 | ui->pushButton_xdb_dstFile->setEnabled(runing == false); 170 | ui->pushButton_xdb_srcFile->setEnabled(runing == false); 171 | ui->pushButton_xdb_start->setEnabled(runing == false); 172 | if (runing) { 173 | ui->pushButton_xdb_start->setText("正在转换..."); 174 | } else { 175 | ui->pushButton_xdb_start->setText("开始转换"); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Ip2regionTool-qt/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "ip2region.h" 6 | 7 | namespace Ui { 8 | class MainWindow; 9 | } 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit MainWindow(QWidget *parent = 0); 17 | ~MainWindow(); 18 | void setTxtToXdb_IsRuning(bool runing); 19 | private slots: 20 | void on_pushButton_input_db_clicked(); 21 | 22 | void on_pushButton_output_txt_clicked(); 23 | 24 | void on_pushButton_input_txt_clicked(); 25 | 26 | void on_pushButton_output_db_clicked(); 27 | 28 | void on_pushButton_DbToTxt_clicked(); 29 | 30 | void on_pushButton_TxtToDb_clicked(); 31 | 32 | void on_tabWidget_currentChanged(int index); 33 | 34 | void on_pushButton_input_regin_csv_clicked(); 35 | 36 | void on_pushButton_xdb_srcFile_clicked(); 37 | 38 | void on_pushButton_xdb_dstFile_clicked(); 39 | 40 | void on_pushButton_xdb_start_clicked(); 41 | 42 | private: 43 | Ui::MainWindow *ui; 44 | RunOnUiThread m_syncUi; 45 | }; 46 | 47 | #endif // MAINWINDOW_H 48 | -------------------------------------------------------------------------------- /Ip2regionTool-qt/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 660 10 | 191 11 | 12 | 13 | 14 | 15 | 660 16 | 175 17 | 18 | 19 | 20 | 21 | 660 22 | 200 23 | 24 | 25 | 26 | Ip2region数据转换工具 27 | 28 | 29 | 30 | 31 | 32 | 33 | 0 34 | 35 | 36 | 37 | db转换为txt 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 输入文件 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 0 57 | 0 58 | 59 | 60 | 61 | 62 | 40 63 | 20 64 | 65 | 66 | 67 | 68 | 40 69 | 20 70 | 71 | 72 | 73 | ... 74 | 75 | 76 | 77 | 78 | 79 | 80 | 输出txt文件 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 0 92 | 0 93 | 94 | 95 | 96 | 97 | 40 98 | 20 99 | 100 | 101 | 102 | 103 | 40 104 | 20 105 | 106 | 107 | 108 | ... 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 自动合并相邻ip段 123 | 124 | 125 | 126 | 127 | 128 | 129 | Qt::Horizontal 130 | 131 | 132 | 133 | 40 134 | 20 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 数据版本: 143 | 144 | 145 | 146 | 147 | 148 | 149 | v1(*.db) 150 | 151 | 152 | true 153 | 154 | 155 | 156 | 157 | 158 | 159 | v2(*.xdb) 160 | 161 | 162 | 163 | 164 | 165 | 166 | Qt::Horizontal 167 | 168 | 169 | 170 | 40 171 | 20 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | Qt::Vertical 182 | 183 | 184 | 185 | 20 186 | 21 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | Qt::Horizontal 197 | 198 | 199 | 200 | 40 201 | 20 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 开始转换 210 | 211 | 212 | 213 | 214 | 215 | 216 | Qt::Horizontal 217 | 218 | 219 | 220 | 40 221 | 20 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | txt转换为db 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | region.csv 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 0 249 | 0 250 | 251 | 252 | 253 | 254 | 40 255 | 20 256 | 257 | 258 | 259 | 260 | 40 261 | 20 262 | 263 | 264 | 265 | ... 266 | 267 | 268 | 269 | 270 | 271 | 272 | 输入txt文件 273 | 274 | 275 | 276 | 277 | 278 | 279 | 输出db文件 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 0 291 | 0 292 | 293 | 294 | 295 | 296 | 40 297 | 20 298 | 299 | 300 | 301 | 302 | 40 303 | 20 304 | 305 | 306 | 307 | ... 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 0 319 | 0 320 | 321 | 322 | 323 | 324 | 40 325 | 20 326 | 327 | 328 | 329 | 330 | 40 331 | 20 332 | 333 | 334 | 335 | ... 336 | 337 | 338 | 339 | 340 | 341 | 342 | 可选填入 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 自动合并相邻ip段 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | Qt::Horizontal 361 | 362 | 363 | 364 | 40 365 | 20 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 开始转换 374 | 375 | 376 | 377 | 378 | 379 | 380 | Qt::Horizontal 381 | 382 | 383 | 384 | 40 385 | 20 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | txt转换为xdb 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 输入txt文件 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 40 428 | 20 429 | 430 | 431 | 432 | 433 | 40 434 | 20 435 | 436 | 437 | 438 | ... 439 | 440 | 441 | 442 | 443 | 444 | 445 | 输出xdb文件 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 40 457 | 20 458 | 459 | 460 | 461 | 462 | 40 463 | 20 464 | 465 | 466 | 467 | ... 468 | 469 | 470 | 471 | 472 | 473 | 474 | IndexPolicy 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | vector 483 | 484 | 485 | 486 | 487 | btree 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | Qt::Vertical 498 | 499 | 500 | 501 | 20 502 | 14 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | Qt::Horizontal 513 | 514 | 515 | 516 | 40 517 | 20 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 开始转换 526 | 527 | 528 | 529 | 530 | 531 | 532 | Qt::Horizontal 533 | 534 | 535 | 536 | 40 537 | 20 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | -------------------------------------------------------------------------------- /Ip2regionTool-qt/version.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orestonce/Ip2regionTool/df7b9be1cdfacaab7cc7e1fe5569f8aeba005304/Ip2regionTool-qt/version.rc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 orestonce 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ip2region 数据转换工具 2 | * 来源项目: https://github.com/lionsoul2014/ip2region 3 | * 旧版的db数据格式: 4 | * 支持 txt -> db, db -> txt 5 | * 支持 region.csv 计算城市id 6 | * 支持合并相邻的ip段, 如果相邻ip段的地址信息相等,则合并 7 | * 新版的 [xdb](https://mp.weixin.qq.com/s?__biz=MzU4MDc2MzQ5OA==&mid=2247483696&idx=1&sn=6e9e138e86cf18245656c54ff4be3129&chksm=fd50ab35ca2722239ae7c0bb08efa44f499110c810227cbad3a16f36ebc1c2afc58eb464a57c#rd) 数据格式 8 | * 支持 txt -> xdb, 包括2种索引模式 9 | * 支持 xdb -> txt 10 | * 二进制需要的运行环境: 11 | * 提供windows_386、linux_386、darwin_amd64 三种版本的命令行二进制 12 | * 提供 windows Qt界面的二进制, 使用qt5.6/go1.12.17编译, 支持windows xp运行二进制 13 | # 下载地址: 14 | * https://github.com/orestonce/Ip2regionTool/releases 15 | 16 | ## 程序运行截图 17 | ![程序截图](image/1.png) 18 | ![程序截图](image/2.png) 19 | ![程序截图](image/3.png) 20 | 21 | * TODO 22 | * [ ] ipip.net 数据格式支持 *.ipx *.ipdb 23 | * [ ] geoip 数据格式支持 24 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/orestonce/Ip2regionTool" 6 | "github.com/spf13/cobra" 7 | "os" 8 | ) 9 | 10 | var rootCmd = &cobra.Command{ 11 | Use: "Ip2regionTool", 12 | } 13 | 14 | func main() { 15 | rootCmd.Execute() 16 | } 17 | 18 | func init() { 19 | var dbFileName string 20 | var txtFileName string 21 | var merge bool 22 | var dbVersion int 23 | 24 | DbToTxtCmd := &cobra.Command{ 25 | Use: "DbToTxt", 26 | Run: func(cmd *cobra.Command, args []string) { 27 | errMsg := Ip2regionTool.ConvertDbToTxt(Ip2regionTool.ConvertDbToTxt_Req{ 28 | DbFileName: dbFileName, 29 | TxtFileName: txtFileName, 30 | Merge: merge, 31 | DbVersion: dbVersion, 32 | }) 33 | if errMsg != `` { 34 | fmt.Println(errMsg) 35 | os.Exit(-1) 36 | } 37 | }, 38 | } 39 | DbToTxtCmd.Flags().StringVarP(&txtFileName, `txt`, ``, "", ``) 40 | DbToTxtCmd.Flags().StringVarP(&dbFileName, `db`, ``, "", ``) 41 | DbToTxtCmd.Flags().IntVarP(&dbVersion, "dbversion", "", 0, "0 -> auto detect, 1 -> v1, 2 -> v2") 42 | DbToTxtCmd.Flags().BoolVarP(&merge, "merge", "", true, "") 43 | rootCmd.AddCommand(DbToTxtCmd) 44 | 45 | var regionCsvvFileName string 46 | 47 | TxtToDbCmd := &cobra.Command{ 48 | Use: "TxtToDb", 49 | Run: func(cmd *cobra.Command, args []string) { 50 | errMsg := Ip2regionTool.ConvertTxtToDb(Ip2regionTool.ConvertTxtToDb_Req{ 51 | TxtFileName: txtFileName, 52 | DbFileName: dbFileName, 53 | RegionCsvFileName: regionCsvvFileName, 54 | Merge: merge, 55 | }) 56 | if errMsg != `` { 57 | fmt.Println(errMsg) 58 | os.Exit(-1) 59 | } 60 | }, 61 | } 62 | TxtToDbCmd.Flags().StringVarP(&txtFileName, `txt`, ``, "", ``) 63 | TxtToDbCmd.Flags().StringVarP(&dbFileName, `db`, ``, "", ``) 64 | TxtToDbCmd.Flags().StringVarP(®ionCsvvFileName, "region", "", "", "") 65 | TxtToDbCmd.Flags().BoolVarP(&merge, "merge", "", true, "") 66 | rootCmd.AddCommand(TxtToDbCmd) 67 | 68 | var srcFile string 69 | var dstFile string 70 | var indexPolicy string 71 | 72 | TxtToXdbCmd := &cobra.Command{ 73 | Use: "TxtToXdb", 74 | Run: func(cmd *cobra.Command, args []string) { 75 | errMsg := Ip2regionTool.TxtToXdb(Ip2regionTool.TxtToXdbReq{ 76 | SrcFile: srcFile, 77 | DstFile: dstFile, 78 | IndexPolicyS: indexPolicy, 79 | }) 80 | if errMsg != "" { 81 | fmt.Println(errMsg) 82 | os.Exit(-1) 83 | } 84 | }, 85 | } 86 | TxtToXdbCmd.Flags().StringVarP(&srcFile, "srcFile", "", "", "") 87 | TxtToXdbCmd.Flags().StringVarP(&dstFile, "dstFile", "", "", "") 88 | TxtToXdbCmd.Flags().StringVarP(&indexPolicy, "indexPolicy", "", "", "[vector/btree]") 89 | rootCmd.AddCommand(TxtToXdbCmd) 90 | } 91 | -------------------------------------------------------------------------------- /export/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/orestonce/Ip2regionTool" 6 | "github.com/orestonce/go2cpp" 7 | "golang.org/x/text/encoding/simplifiedchinese" 8 | "io/ioutil" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | func main() { 17 | ctx := go2cpp.NewGo2cppContext(go2cpp.NewGo2cppContext_Req{ 18 | CppBaseName: "ip2region", 19 | EnableQtClass_RunOnUiThread: true, 20 | EnableQtClass_Toast: true, 21 | }) 22 | ctx.Generate1(Ip2regionTool.ConvertDbToTxt) 23 | ctx.Generate1(Ip2regionTool.GetDbVersionByName) 24 | ctx.Generate1(Ip2regionTool.ConvertTxtToDb) 25 | ctx.Generate1(Ip2regionTool.TxtToXdb) 26 | 27 | if os.Getenv("GITHUB_ACTIONS") == "" { // 正常编译 28 | ctx.MustCreateAmd64LibraryInDir("Ip2regionTool-qt") 29 | } else { // github actions 编译 30 | version := strings.TrimPrefix(os.Getenv("GITHUB_REF_NAME"), "v") 31 | urlStr := "https://github.com/" + os.Getenv("GITHUB_REPOSITORY") 32 | ctx.MustCreate386LibraryInDir("Ip2regionTool-qt") 33 | WriteVersionDotRc(WriteVersionDotRc_Req{ 34 | Version: version, 35 | ProductName: "Ip2region数据转换工具", 36 | CopyRightUrl: urlStr, 37 | OutputRcName: "Ip2regionTool-qt/version.rc", 38 | }) 39 | wd, err := os.Getwd() 40 | if err != nil { 41 | panic(err) 42 | } 43 | type buildCfg struct { 44 | GOOS string 45 | GOARCH string 46 | Ext string 47 | } 48 | var list = []buildCfg{ 49 | { 50 | GOOS: "linux", 51 | GOARCH: "386", 52 | }, 53 | { 54 | GOOS: "darwin", 55 | GOARCH: "amd64", 56 | }, 57 | { 58 | GOOS: "windows", 59 | GOARCH: "386", 60 | Ext: ".exe", 61 | }, 62 | } 63 | for _, cfg := range list { 64 | name := "Ip2regionTool_cli_" + cfg.GOOS + "_" + cfg.GOARCH + cfg.Ext 65 | cmd := exec.Command("go", "build", "-ldflags", "-s -w", "-o", filepath.Join(wd, "bin", name)) // "-trimpath" 66 | cmd.Dir = filepath.Join(wd, "cmd") 67 | cmd.Env = append(os.Environ(), "GOOS="+cfg.GOOS) 68 | cmd.Env = append(cmd.Env, "GOARCH="+cfg.GOARCH) 69 | cmd.Stdout = os.Stdout 70 | cmd.Stderr = os.Stderr 71 | err = cmd.Run() 72 | if err != nil { 73 | fmt.Println(cmd.Dir) 74 | panic(err) 75 | } 76 | fmt.Println("done", name) 77 | } 78 | } 79 | 80 | } 81 | 82 | type WriteVersionDotRc_Req struct { 83 | Version string 84 | ProductName string 85 | CopyRightUrl string 86 | OutputRcName string 87 | } 88 | 89 | func WriteVersionDotRc(req WriteVersionDotRc_Req) { 90 | tmp := strings.Split(req.Version, ".") 91 | ok := len(tmp) == 3 92 | for _, v := range tmp { 93 | vi, err := strconv.Atoi(v) 94 | if err != nil { 95 | ok = false 96 | break 97 | } 98 | if vi < 0 { 99 | ok = false 100 | break 101 | } 102 | } 103 | if ok == false { 104 | panic("version invalid: " + strconv.Quote(req.Version)) 105 | } 106 | tmp = append(tmp, "0") 107 | v1 := strings.Join(tmp, ",") 108 | data := []byte(`IDI_ICON1 ICON "favicon.ico" 109 | 110 | #if defined(UNDER_CE) 111 | #include 112 | #else 113 | #include 114 | #endif 115 | 116 | VS_VERSION_INFO VERSIONINFO 117 | FILEVERSION ` + v1 + ` 118 | PRODUCTVERSION ` + v1 + ` 119 | FILEFLAGSMASK 0x3fL 120 | #ifdef _DEBUG 121 | FILEFLAGS VS_FF_DEBUG 122 | #else 123 | FILEFLAGS 0x0L 124 | #endif 125 | FILEOS VOS__WINDOWS32 126 | FILETYPE VFT_DLL 127 | FILESUBTYPE 0x0L 128 | BEGIN 129 | BLOCK "StringFileInfo" 130 | BEGIN 131 | BLOCK "080404b0" 132 | BEGIN 133 | VALUE "ProductVersion", "` + req.Version + `.0\0" 134 | VALUE "ProductName", "` + req.ProductName + `\0" 135 | VALUE "LegalCopyright", "https://github.com/orestonce/m3u8d\0" 136 | VALUE "FileDescription", "` + req.ProductName + `\0" 137 | END 138 | END 139 | 140 | BLOCK "VarFileInfo" 141 | BEGIN 142 | VALUE "Translation", 0x804, 1200 143 | END 144 | END 145 | `) 146 | data, err := simplifiedchinese.GBK.NewEncoder().Bytes(data) 147 | if err != nil { 148 | panic(err) 149 | } 150 | err = ioutil.WriteFile(req.OutputRcName, data, 0777) 151 | if err != nil { 152 | panic(err) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/orestonce/Ip2regionTool 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07 7 | github.com/spf13/cobra v1.2.1 8 | golang.org/x/text v0.3.5 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 17 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 18 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 19 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 20 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 21 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 22 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 23 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 24 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 25 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 26 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 27 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 28 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 29 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 30 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 31 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 32 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 33 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 34 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 35 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 36 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 37 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 38 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 39 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 40 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 41 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 42 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 43 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 44 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 45 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 46 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 47 | github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 48 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 49 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 50 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 51 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 52 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 53 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 54 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 55 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 56 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 57 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 58 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 59 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 60 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 61 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 62 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 63 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 64 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 65 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 66 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 67 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 68 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 69 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 70 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 71 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 72 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 73 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 74 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 75 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 76 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 77 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 78 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 79 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 80 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 81 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 82 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 83 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 84 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 85 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 86 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 87 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 88 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 89 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 90 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 91 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 92 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 93 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 94 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 95 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 96 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 97 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 98 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 99 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 100 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 101 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 102 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 103 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 104 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 105 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 106 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 107 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 108 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 109 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 110 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 111 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 112 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 113 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 114 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 115 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 116 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 117 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 118 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 119 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 120 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 121 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 122 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 123 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 124 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 125 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 126 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 127 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 128 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 129 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 130 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 131 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 132 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 133 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 134 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 135 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 136 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 137 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 138 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 139 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 140 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 141 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 142 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 143 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 144 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 145 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 146 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 147 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 148 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 149 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 150 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 151 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 152 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 153 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 154 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 155 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 156 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 157 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 158 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 159 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 160 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 161 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 162 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 163 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 164 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 165 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 166 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 167 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 168 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 169 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 170 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 171 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 172 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 173 | github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 174 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 175 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 176 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 177 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 178 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 179 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 180 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 181 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 182 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 183 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 184 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 185 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 186 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 187 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 188 | github.com/orestonce/go2cpp v0.0.0-20220730064838-feb9dd043919 h1:f8oUxbDjOgXrBhtDSaNWNAnZEPDTwrjpccdsrn4UCUs= 189 | github.com/orestonce/go2cpp v0.0.0-20220730064838-feb9dd043919/go.mod h1:1fsOAZftk08/dOTRqlp6f/MVwaEKOhrnPUg0RtWiSdY= 190 | github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07 h1:nyt0GDiskod5Y9uNVrXgK7PZHeL7Ab1uVc3LhLg7/gk= 191 | github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07/go.mod h1:1fsOAZftk08/dOTRqlp6f/MVwaEKOhrnPUg0RtWiSdY= 192 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 193 | github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 194 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 195 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 196 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 197 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 198 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 199 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 200 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 201 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 202 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 203 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 204 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 205 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 206 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 207 | github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 208 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 209 | github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= 210 | github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 211 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 212 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 213 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 214 | github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 215 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 216 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 217 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 218 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 219 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 220 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 221 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 222 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 223 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 224 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 225 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 226 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 227 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 228 | go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 229 | go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 230 | go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= 231 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 232 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 233 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 234 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 235 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 236 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 237 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 238 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 239 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 240 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 241 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 242 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 243 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 244 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 245 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 246 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 247 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 248 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 249 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 250 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 251 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 252 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 253 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 254 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 255 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 256 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 257 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 258 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 259 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 260 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 261 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 262 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 263 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 264 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 265 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 266 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 267 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 268 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 269 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 270 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 271 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 272 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 273 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 274 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 275 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 276 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 277 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 278 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 279 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 280 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 281 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 282 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 283 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 284 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 285 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 286 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 287 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 288 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 289 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 290 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 291 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 292 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 293 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 294 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 295 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 296 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 297 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 298 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 299 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 300 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 301 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 302 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 303 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 304 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 305 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 306 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 307 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 308 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 309 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 310 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 311 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 312 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 313 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 314 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 315 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 316 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 317 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 318 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 319 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 320 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 321 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 322 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 323 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 324 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 325 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 326 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 327 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 328 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 329 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 330 | golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 331 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 332 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 333 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 334 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 335 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 336 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 337 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 338 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 339 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 340 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 341 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 342 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 343 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 344 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 345 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 346 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 347 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 348 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 349 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 350 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 351 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 352 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 353 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 354 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 355 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 356 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 357 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 358 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 359 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 360 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 361 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 362 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 363 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 364 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 365 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 366 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 367 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 368 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 369 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 382 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 383 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 384 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 385 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 386 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 387 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 388 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 389 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 390 | golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= 391 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 392 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 393 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 394 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 395 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 396 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 397 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 398 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 399 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 400 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 401 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 402 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 403 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 404 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 405 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 406 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 407 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 408 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 409 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 410 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 411 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 412 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 413 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 414 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 415 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 416 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 417 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 418 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 419 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 420 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 421 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 422 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 423 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 424 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 425 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 426 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 427 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 428 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 429 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 430 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 431 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 432 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 433 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 434 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 435 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 436 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 437 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 438 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 439 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 440 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 441 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 442 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 443 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 444 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 445 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 446 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 447 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 448 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 449 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 450 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 451 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 452 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 453 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 454 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 455 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 456 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 457 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 458 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 459 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 460 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 461 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 462 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 463 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 464 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 465 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 466 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 467 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 468 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 469 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 470 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 471 | google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= 472 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 473 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 474 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 475 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 476 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 477 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 478 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 479 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 480 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 481 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 482 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 483 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 484 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 485 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 486 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 487 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 488 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 489 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 490 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 491 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 492 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 493 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 494 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 495 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 496 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 497 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 498 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 499 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 500 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 501 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 502 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 503 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 504 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 505 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 506 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 507 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 508 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 509 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 510 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 511 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 512 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 513 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 514 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 515 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 516 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 517 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 518 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 519 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 520 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 521 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 522 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 523 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 524 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 525 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 526 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 527 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 528 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 529 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 530 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 531 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 532 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 533 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 534 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 535 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 536 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 537 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 538 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 539 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 540 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 541 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 542 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 543 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 544 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 545 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 546 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 547 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 548 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 549 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 550 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 551 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 552 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 553 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 554 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 555 | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 556 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 557 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 558 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 559 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 560 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 561 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 562 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 563 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 564 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 565 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 566 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 567 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 568 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 569 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 570 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 571 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 572 | -------------------------------------------------------------------------------- /image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orestonce/Ip2regionTool/df7b9be1cdfacaab7cc7e1fe5569f8aeba005304/image/1.png -------------------------------------------------------------------------------- /image/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orestonce/Ip2regionTool/df7b9be1cdfacaab7cc7e1fe5569f8aeba005304/image/2.png -------------------------------------------------------------------------------- /image/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orestonce/Ip2regionTool/df7b9be1cdfacaab7cc7e1fe5569f8aeba005304/image/3.png -------------------------------------------------------------------------------- /tool.go: -------------------------------------------------------------------------------- 1 | package Ip2regionTool 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/csv" 7 | "io/ioutil" 8 | "math" 9 | "net" 10 | "os" 11 | "sort" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | type ConvertDbToTxt_Req struct { 17 | DbFileName string 18 | TxtFileName string 19 | Merge bool 20 | DbVersion int 21 | } 22 | 23 | func GetDbVersionByName(name string) int { 24 | if strings.HasSuffix(strings.ToLower(name), ".db") { 25 | return 1 26 | } 27 | if strings.HasSuffix(strings.ToLower(name), ".xdb") { 28 | return 2 29 | } 30 | return 1 31 | } 32 | 33 | func ConvertDbToTxt(req ConvertDbToTxt_Req) (errMsg string) { 34 | if req.DbVersion == 0 { 35 | req.DbVersion = GetDbVersionByName(req.DbFileName) 36 | } 37 | stat, err := os.Stat(req.DbFileName) 38 | if err != nil { 39 | return "文件状态错误: " + req.DbFileName + "," + err.Error() 40 | } 41 | if stat.Size() > 1000*1024*1024 { 42 | return "不支持超过1000MB的db文件: " + strconv.Itoa(int(stat.Size())) 43 | } 44 | dbFileContent, err := ioutil.ReadFile(req.DbFileName) 45 | if err != nil { 46 | return "读取db文件失败: " + req.DbFileName + ", " + err.Error() 47 | } 48 | var list []IpRangeItem 49 | if req.DbVersion == 1 { 50 | list, errMsg = ReadV1DataBlob(dbFileContent) 51 | } else { 52 | list, errMsg = ReadV2DataBlob(dbFileContent) 53 | } 54 | if errMsg != `` { 55 | return "文件数据错误: " + errMsg 56 | } 57 | if req.Merge { 58 | list = MergeIpRangeList(list) 59 | } 60 | errMsg = VerifyIpRangeList(VerifyIpRangeListRequest{ 61 | DataInfoList: list, 62 | VerifyFullUint32: true, 63 | VerifyFiled7: req.DbVersion == 1, // 只有版本1才需要验证字段数为7 64 | }) 65 | if errMsg != `` { 66 | return "验证文件数据失败: " + errMsg 67 | } 68 | data := WriteV1DataTxt(list) 69 | err = ioutil.WriteFile(req.TxtFileName, data, 0777) 70 | if err != nil { 71 | return "输出文件写入失败: " + err.Error() 72 | } 73 | return "" 74 | } 75 | 76 | type ConvertTxtToDb_Req struct { 77 | TxtFileName string 78 | DbFileName string 79 | RegionCsvFileName string 80 | Merge bool 81 | } 82 | 83 | func ConvertTxtToDb(req ConvertTxtToDb_Req) (errMsg string) { 84 | stat, err := os.Stat(req.TxtFileName) 85 | if err != nil { 86 | return "文件状态错误: " + req.DbFileName + "," + err.Error() 87 | } 88 | if stat.Size() > 1000*1024*1024 { 89 | return "不支持超过1000MB的db文件: " + strconv.Itoa(int(stat.Size())) 90 | } 91 | var globalRegionMap map[string]uint32 92 | if req.RegionCsvFileName != "" { 93 | globalRegionMap, errMsg = ReadGlobalRegionMap(req.RegionCsvFileName) 94 | if errMsg != "" { 95 | return errMsg 96 | } 97 | } 98 | txtFileContent, err := ioutil.ReadFile(req.TxtFileName) 99 | if err != nil { 100 | return "读取db文件失败: " + req.TxtFileName + ", " + err.Error() 101 | } 102 | list := ReadV1DataTxt(txtFileContent) 103 | if errMsg != `` { 104 | return "文件数据错误: " + errMsg 105 | } 106 | if req.Merge { 107 | list = MergeIpRangeList(list) 108 | } 109 | if len(globalRegionMap) > 0 { 110 | for idx, one := range list { 111 | cityId := GetCityId(one.Attach, globalRegionMap) 112 | list[idx].CityId = cityId 113 | } 114 | } 115 | errMsg = VerifyIpRangeList(VerifyIpRangeListRequest{ 116 | DataInfoList: list, 117 | VerifyFullUint32: true, 118 | VerifyFiled7: true, 119 | }) 120 | if errMsg != `` { 121 | return "验证文件数据失败: " + errMsg 122 | } 123 | data := WriteV1DataBlob(list) 124 | err = ioutil.WriteFile(req.DbFileName, data, 0777) 125 | if err != nil { 126 | return "输出文件写入失败: " + err.Error() 127 | } 128 | return "" 129 | } 130 | 131 | func ReadGlobalRegionMap(regionCsv string) (globalRegionMap map[string]uint32, errMsg string) { 132 | regionData, err := ioutil.ReadFile(regionCsv) 133 | if err != nil { 134 | return nil, "读取region.csv失败: " + err.Error() 135 | } 136 | var recordAll [][]string 137 | recordAll, err = csv.NewReader(bytes.NewReader(regionData)).ReadAll() 138 | if err != nil { 139 | return nil, "读取region.csv失败2: " + err.Error() 140 | } 141 | globalRegionMap = map[string]uint32{} 142 | for _, line := range recordAll { 143 | if len(line) != 5 { 144 | continue 145 | } 146 | cityId, _ := strconv.Atoi(line[0]) 147 | name := line[2] 148 | globalRegionMap[name] = uint32(cityId) 149 | } 150 | return globalRegionMap, "" 151 | } 152 | 153 | func GetCityId(region string, globalRegionMap map[string]uint32) uint32 { 154 | var p = strings.Split(region, "|") 155 | if len(p) != 5 { 156 | return 0 157 | } 158 | var key string 159 | for i := 3; i >= 0; i-- { 160 | if p[i] == "0" { 161 | continue 162 | } 163 | key = p[i] 164 | return globalRegionMap[key] 165 | } 166 | return 0 167 | } 168 | 169 | type IpRangeItem struct { 170 | Origin string 171 | LowU32 uint32 172 | HighU32 uint32 173 | Attach string 174 | CityId uint32 175 | } 176 | 177 | func ReadV1DataTxt(data []byte) (list []IpRangeItem) { 178 | for _, one := range strings.Split(string(data), "\n") { 179 | one = strings.TrimSpace(one) 180 | if one == `` { 181 | continue 182 | } 183 | temp := strings.Split(one, `|`) 184 | if len(temp) < 2 { 185 | continue 186 | } 187 | sip := ipToUint32(net.ParseIP(temp[0])) 188 | eip := ipToUint32(net.ParseIP(temp[1])) 189 | list = append(list, IpRangeItem{ 190 | Origin: one, 191 | LowU32: sip, 192 | HighU32: eip, 193 | Attach: strings.Join(temp[2:], `|`), 194 | }) 195 | } 196 | return list 197 | } 198 | 199 | func WriteV1DataTxt(list []IpRangeItem) (data []byte) { 200 | buf := bytes.NewBuffer(nil) 201 | for _, one := range list { 202 | buf.WriteString(uint32ToIp(one.LowU32).String() + `|` + uint32ToIp(one.HighU32).String() + `|` + one.Attach + "\n") 203 | } 204 | return buf.Bytes() 205 | } 206 | 207 | func WriteV1DataBlob(list []IpRangeItem) (data []byte) { 208 | idxMap := map[string]uint32{} 209 | data = make([]byte, 8) 210 | for _, one := range list { 211 | if idxMap[one.Attach] > 0 { 212 | continue 213 | } 214 | idxMap[one.Attach] = uint32(len(data)) | uint32((len(one.Attach)+4)<<24) 215 | cityIdBytes := make([]byte, 4) 216 | binary.LittleEndian.PutUint32(cityIdBytes, one.CityId) 217 | data = append(data, cityIdBytes...) 218 | data = append(data, one.Attach...) 219 | } 220 | binary.LittleEndian.PutUint32(data[0:], uint32(len(data))) 221 | sort.Slice(list, func(i, j int) bool { 222 | return list[i].LowU32 < list[j].LowU32 223 | }) 224 | for _, one := range list { 225 | tmp := make([]byte, 12) 226 | binary.LittleEndian.PutUint32(tmp[0:], one.LowU32) 227 | binary.LittleEndian.PutUint32(tmp[4:], one.HighU32) 228 | binary.LittleEndian.PutUint32(tmp[8:], idxMap[one.Attach]) 229 | data = append(data, tmp...) 230 | } 231 | binary.LittleEndian.PutUint32(data[4:], uint32(len(data)-12)) 232 | return data 233 | } 234 | 235 | func ReadV1DataBlob(b []byte) (list []IpRangeItem, errMsg string) { 236 | if len(b) < 8 { 237 | return nil, "数据文件大小至少为8字节" 238 | } 239 | fp := getUint32(b, 0) 240 | lp := getUint32(b, 4) 241 | if fp < 8 || lp > uint32(len(b))-12 || fp > lp || (lp-fp)%12 != 0 { 242 | return nil, "lp, fp 指针异常 " + strconv.Itoa(int(fp)) + ", " + strconv.Itoa(int(lp)) 243 | } 244 | 245 | var dataInfoList []IpRangeItem 246 | 247 | for idx := fp; idx <= lp; idx += 12 { 248 | ptr := getUint32(b, idx+8) 249 | attach := `` 250 | dataLen := (ptr >> 24) & 0xFF 251 | if dataLen > math.MaxUint8 { 252 | return nil, "附加数据长度异常1 " + strconv.Itoa(int(dataLen)) 253 | } 254 | var cityId uint32 255 | if dataLen > 0 { 256 | if dataLen < 4 { 257 | return nil, "附加数据长度异常2 " + strconv.Itoa(int(dataLen)) 258 | } 259 | ptr = ptr & 0x00FFFFFF 260 | if ptr+dataLen > uint32(len(b)) { 261 | return nil, "附加数据长度异常3 " + strconv.Itoa(int(ptr)) + "," + strconv.Itoa(int(dataLen)) + "," + strconv.Itoa(len(b)) 262 | } 263 | attach = string(b[ptr+4 : ptr+dataLen]) 264 | cityId = binary.LittleEndian.Uint32(b[ptr:]) 265 | } 266 | dataInfoList = append(dataInfoList, IpRangeItem{ 267 | LowU32: getUint32(b, idx), 268 | HighU32: getUint32(b, idx+4), 269 | Attach: attach, 270 | CityId: cityId, 271 | }) 272 | } 273 | return dataInfoList, "" 274 | } 275 | 276 | type VerifyIpRangeListRequest struct { 277 | DataInfoList []IpRangeItem 278 | VerifyFullUint32 bool // 验证是否全部的uint32的ip都已覆盖 279 | VerifyFiled7 bool // 验证是否每行都有7个字段 280 | } 281 | 282 | func VerifyIpRangeList(req VerifyIpRangeListRequest) (errMsg string) { 283 | for idx := 0; idx < len(req.DataInfoList)-1; idx++ { 284 | left := req.DataInfoList[idx] 285 | right := req.DataInfoList[idx+1] 286 | 287 | if left.LowU32 >= right.LowU32 { 288 | return "ip范围未排序: " + left.Origin 289 | } 290 | } 291 | for _, one := range req.DataInfoList { 292 | if one.LowU32 > one.LowU32 { 293 | return "ip范围信息错误, 第一个ip必须小于等于第二个ip: " + one.Origin 294 | } 295 | if req.VerifyFiled7 && len(strings.Split(one.Attach, `|`)) != 5 { 296 | return "ip范围信息错误,需要有7个字段: " + one.Origin 297 | } 298 | } 299 | if req.VerifyFullUint32 { 300 | if len(req.DataInfoList) == 0 || req.DataInfoList[0].LowU32 != 0 { 301 | return "ip 范围缺失[0.0.0.0, ~]" 302 | } 303 | if req.DataInfoList[len(req.DataInfoList)-1].HighU32 != math.MaxUint32 { 304 | return "ip 范围缺失 [~, 255.255.255.255]" 305 | } 306 | for idx := 0; idx < len(req.DataInfoList)-1; idx++ { 307 | left := req.DataInfoList[idx] 308 | right := req.DataInfoList[idx+1] 309 | 310 | if left.HighU32+1 != right.LowU32 { 311 | return "ip范围缺失, [" + uint32ToIp(left.HighU32+1).String() + `, ` + uint32ToIp(right.LowU32-1).String() + "]" 312 | } 313 | } 314 | } 315 | return "" 316 | } 317 | 318 | func uint32ToIp(ip uint32) net.IP { 319 | var tmp = make([]byte, 4) 320 | binary.BigEndian.PutUint32(tmp, ip) 321 | return net.IPv4(tmp[0], tmp[1], tmp[2], tmp[3]) 322 | } 323 | 324 | func ipToUint32(ip net.IP) uint32 { 325 | ip = ip.To4() 326 | return binary.BigEndian.Uint32([]byte{ip[0], ip[1], ip[2], ip[3]}) 327 | } 328 | 329 | func getUint32(b []byte, offset uint32) uint32 { 330 | return binary.LittleEndian.Uint32(b[offset:]) 331 | } 332 | 333 | func MergeIpRangeList(list []IpRangeItem) []IpRangeItem { 334 | listLen := len(list) 335 | merge := make([]IpRangeItem, 0, listLen) 336 | 337 | for idx := 0; idx < listLen; idx++ { 338 | mergeLen := len(merge) 339 | if idx > 0 && merge[mergeLen-1].Attach == list[idx].Attach && merge[mergeLen-1].HighU32+1 == list[idx].LowU32 { 340 | merge[mergeLen-1].HighU32 = list[idx].HighU32 341 | continue 342 | } 343 | 344 | merge = append(merge, list[idx]) 345 | } 346 | 347 | return merge 348 | } 349 | -------------------------------------------------------------------------------- /xdb.go: -------------------------------------------------------------------------------- 1 | package Ip2regionTool 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | type TxtToXdbReq struct { 10 | SrcFile string 11 | DstFile string 12 | IndexPolicyS string 13 | } 14 | 15 | func TxtToXdb(req TxtToXdbReq) (errMsg string) { 16 | indexPolicy, err := IndexPolicyFromString(req.IndexPolicyS) 17 | if err != nil { 18 | return "indexPolicy " + req.IndexPolicyS 19 | } 20 | maker, err := NewMaker(indexPolicy, req.SrcFile, req.DstFile) 21 | if err != nil { 22 | return fmt.Sprintf("failed to create %s", err) 23 | } 24 | defer maker.End() 25 | 26 | err = maker.Init() 27 | if err != nil { 28 | return fmt.Sprintf("failed Init: %s", err) 29 | } 30 | 31 | err = maker.Start() 32 | if err != nil { 33 | return fmt.Sprintf("failed Start: %s", err) 34 | } 35 | 36 | err = maker.End() 37 | if err != nil { 38 | return fmt.Sprintf("failed End: %s", err) 39 | } 40 | return "" 41 | } 42 | 43 | // 44 | //type XdbToTxtReq struct { 45 | // InputXdbFile string 46 | // OutputTxtFile string 47 | //} 48 | // 49 | //func XdbToTxt(req XdbToTxtReq) (errMsg string) { 50 | // xdbData, err := ioutil.ReadFile(req.InputXdbFile) 51 | // if err != nil { 52 | // return "XdbToTxt 读取错误: " + err.Error() 53 | // } 54 | // list, errMsg := ReadV2DataBlob(xdbData) 55 | // if errMsg != "" { 56 | // return "XdbToTxt 解析错误: " + errMsg 57 | // } 58 | // txtData := WriteV1DataTxt(list) 59 | // err = ioutil.WriteFile(req.OutputTxtFile, txtData, 0666) 60 | // if err != nil { 61 | // return "XdbToTxt 写入错误: " + err.Error() 62 | // } 63 | // return "" 64 | //} 65 | 66 | func ReadV2DataBlob(b []byte) (list []IpRangeItem, errMsg string) { 67 | const hLen = 256 + 512*1024 68 | if len(b) < hLen { 69 | return nil, "header length error: " + strconv.Itoa(len(b)) 70 | } 71 | startIndexPtr := binary.LittleEndian.Uint32(b[8:]) 72 | endIndexPtr := binary.LittleEndian.Uint32(b[12:]) 73 | if startIndexPtr < hLen || startIndexPtr >= uint32(len(b)) || endIndexPtr >= uint32(len(b)) || startIndexPtr > endIndexPtr { 74 | return nil, "startIndexPtr/endIndexPtr length error: " + strconv.Itoa(int(startIndexPtr)) + ", " + strconv.Itoa(int(endIndexPtr)) 75 | } 76 | for ptr := startIndexPtr; ptr <= endIndexPtr; ptr += SegmentIndexSize { 77 | indexBuff := b[ptr:] 78 | var item IpRangeItem 79 | item.LowU32 = binary.LittleEndian.Uint32(indexBuff) 80 | item.HighU32 = binary.LittleEndian.Uint32(indexBuff[4:]) 81 | dataLen := int(binary.LittleEndian.Uint16(indexBuff[8:])) 82 | dataPtr := int(binary.LittleEndian.Uint32(indexBuff[10:])) 83 | if dataPtr+dataLen > len(b) { 84 | return nil, "dataPtr/dataLen error: " + strconv.Itoa(dataPtr) + ", " + strconv.Itoa(dataLen) 85 | } 86 | item.Attach = string(b[dataPtr : dataPtr+dataLen]) 87 | list = append(list, item) 88 | } 89 | return list, "" 90 | } 91 | -------------------------------------------------------------------------------- /xdb_index.go: -------------------------------------------------------------------------------- 1 | package Ip2regionTool 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type IndexPolicy int 9 | 10 | const ( 11 | VectorIndexPolicy IndexPolicy = 1 12 | BTreeIndexPolicy IndexPolicy = 2 13 | ) 14 | 15 | func IndexPolicyFromString(str string) (IndexPolicy, error) { 16 | switch strings.ToLower(str) { 17 | case "vector": 18 | return VectorIndexPolicy, nil 19 | case "btree": 20 | return BTreeIndexPolicy, nil 21 | default: 22 | return VectorIndexPolicy, fmt.Errorf("invalid policy '%s'", str) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xdb_maker.go: -------------------------------------------------------------------------------- 1 | package Ip2regionTool 2 | 3 | import ( 4 | "bufio" 5 | "encoding/binary" 6 | "fmt" 7 | "io" 8 | "os" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // Copyright 2022 The Ip2Region Authors. All rights reserved. 14 | // Use of this source code is governed by a Apache2.0-style 15 | // license that can be found in the LICENSE file. 16 | 17 | // ---- 18 | // ip2region database v2.0 structure 19 | // 20 | // +----------------+-------------------+---------------+--------------+ 21 | // | header space | speed up index | data payload | block index | 22 | // +----------------+-------------------+---------------+--------------+ 23 | // | 256 bytes | 512 KiB (fixed) | dynamic size | dynamic size | 24 | // +----------------+-------------------+---------------+--------------+ 25 | // 26 | // 1. padding space : for header info like block index ptr, version, release date eg ... or any other temporary needs. 27 | // -- 2bytes: version number, different version means structure update, it fixed to 2 for now 28 | // -- 2bytes: index algorithm code. 29 | // -- 4bytes: generate unix timestamp (version) 30 | // -- 4bytes: index block start ptr 31 | // -- 4bytes: index block end ptr 32 | // 33 | // 34 | // 2. data block : region or whatever data info. 35 | // 3. segment index block : binary index block. 36 | // 4. vector index block : fixed index info for block index search speed up. 37 | // space structure table: 38 | // -- 0 -> | 1rt super block | 2nd super block | 3rd super block | ... | 255th super block 39 | // -- 1 -> | 1rt super block | 2nd super block | 3rd super block | ... | 255th super block 40 | // -- 2 -> | 1rt super block | 2nd super block | 3rd super block | ... | 255th super block 41 | // -- ... 42 | // -- 255 -> | 1rt super block | 2nd super block | 3rd super block | ... | 255th super block 43 | // 44 | // 45 | // super block structure: 46 | // +-----------------------+----------------------+ 47 | // | first index block ptr | last index block ptr | 48 | // +-----------------------+----------------------+ 49 | // 50 | // data entry structure: 51 | // +--------------------+-----------------------+ 52 | // | 2bytes (for desc) | dynamic length | 53 | // +--------------------+-----------------------+ 54 | // data length whatever in bytes 55 | // 56 | // index entry structure 57 | // +------------+-----------+---------------+------------+ 58 | // | 4bytes | 4bytes | 2bytes | 4 bytes | 59 | // +------------+-----------+---------------+------------+ 60 | // start ip end ip data length data ptr 61 | 62 | const VersionNo = 2 63 | const HeaderInfoLength = 256 64 | const VectorIndexRows = 256 65 | const VectorIndexCols = 256 66 | const VectorIndexSize = 8 67 | const SegmentIndexSize = 14 68 | const VectorIndexLength = VectorIndexRows * VectorIndexCols * VectorIndexSize 69 | 70 | type Maker struct { 71 | srcHandle *os.File 72 | dstHandle *os.File 73 | 74 | indexPolicy IndexPolicy 75 | segments []*Segment 76 | regionPool map[string]uint32 77 | vectorIndex []byte 78 | } 79 | 80 | func NewMaker(policy IndexPolicy, srcFile string, dstFile string) (*Maker, error) { 81 | // open the source file with READONLY mode 82 | srcHandle, err := os.OpenFile(srcFile, os.O_RDONLY, 0600) 83 | if err != nil { 84 | return nil, fmt.Errorf("open source file `%s`: %w", srcFile, err) 85 | } 86 | 87 | // open the destination file with Read/Write mode 88 | dstHandle, err := os.OpenFile(dstFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 89 | if err != nil { 90 | return nil, fmt.Errorf("open target file `%s`: %w", dstFile, err) 91 | } 92 | 93 | return &Maker{ 94 | srcHandle: srcHandle, 95 | dstHandle: dstHandle, 96 | 97 | indexPolicy: policy, 98 | segments: []*Segment{}, 99 | regionPool: map[string]uint32{}, 100 | vectorIndex: make([]byte, VectorIndexLength), 101 | }, nil 102 | } 103 | 104 | func (m *Maker) initDbHeader() error { 105 | 106 | _, err := m.dstHandle.Seek(0, 0) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | // make and write the header space 112 | var header = make([]byte, 256) 113 | 114 | // 1, version number 115 | binary.LittleEndian.PutUint16(header, uint16(VersionNo)) 116 | 117 | // 2, index policy code 118 | binary.LittleEndian.PutUint16(header[2:], uint16(m.indexPolicy)) 119 | 120 | // 3, generate unix timestamp 121 | binary.LittleEndian.PutUint32(header[4:], uint32(time.Now().Unix())) 122 | 123 | // 4, index block start ptr 124 | binary.LittleEndian.PutUint32(header[8:], uint32(0)) 125 | 126 | // 5, index block end ptr 127 | binary.LittleEndian.PutUint32(header[12:], uint32(0)) 128 | 129 | _, err = m.dstHandle.Write(header) 130 | if err != nil { 131 | return err 132 | } 133 | 134 | return nil 135 | } 136 | 137 | func (m *Maker) loadSegments() error { 138 | var last *Segment = nil 139 | 140 | var scanner = bufio.NewScanner(m.srcHandle) 141 | scanner.Split(bufio.ScanLines) 142 | for scanner.Scan() { 143 | var l = strings.TrimSpace(strings.TrimSuffix(scanner.Text(), "\n")) 144 | 145 | var ps = strings.SplitN(l, "|", 3) 146 | if len(ps) != 3 { 147 | return fmt.Errorf("invalid ip segment line `%s`", l) 148 | } 149 | 150 | sip, err := CheckIP(ps[0]) 151 | if err != nil { 152 | return fmt.Errorf("check start ip `%s`: %s", ps[0], err) 153 | } 154 | 155 | eip, err := CheckIP(ps[1]) 156 | if err != nil { 157 | return fmt.Errorf("check end ip `%s`: %s", ps[1], err) 158 | } 159 | 160 | if sip > eip { 161 | return fmt.Errorf("start ip(%s) should not be greater than end ip(%s)", ps[0], ps[1]) 162 | } 163 | 164 | if len(ps[2]) < 1 { 165 | return fmt.Errorf("empty region info in segment line `%s`", l) 166 | } 167 | 168 | var seg = &Segment{ 169 | StartIP: sip, 170 | EndIP: eip, 171 | Region: ps[2], 172 | } 173 | 174 | // check the continuity of the data segment 175 | if last != nil { 176 | if last.EndIP+1 != seg.StartIP { 177 | return fmt.Errorf("discontinuous data segment: last.eip+1(%d) != seg.sip(%d, %s)", sip, eip, ps[0]) 178 | } 179 | } 180 | 181 | m.segments = append(m.segments, seg) 182 | last = seg 183 | } 184 | 185 | return nil 186 | } 187 | 188 | // Init the db binary file 189 | func (m *Maker) Init() error { 190 | // init the db header 191 | err := m.initDbHeader() 192 | if err != nil { 193 | return fmt.Errorf("init db header: %w", err) 194 | } 195 | 196 | // load all the segments 197 | err = m.loadSegments() 198 | if err != nil { 199 | return fmt.Errorf("load segments: %w", err) 200 | } 201 | 202 | return nil 203 | } 204 | 205 | // refresh the vector index of the specified ip 206 | func (m *Maker) setVectorIndex(ip uint32, ptr uint32) { 207 | var il0 = (ip >> 24) & 0xFF 208 | var il1 = (ip >> 16) & 0xFF 209 | var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize 210 | var sPtr = binary.LittleEndian.Uint32(m.vectorIndex[idx:]) 211 | if sPtr == 0 { 212 | binary.LittleEndian.PutUint32(m.vectorIndex[idx:], ptr) 213 | binary.LittleEndian.PutUint32(m.vectorIndex[idx+4:], ptr+SegmentIndexSize) 214 | } else { 215 | binary.LittleEndian.PutUint32(m.vectorIndex[idx+4:], ptr+SegmentIndexSize) 216 | } 217 | } 218 | 219 | // Start to make the binary file 220 | func (m *Maker) Start() error { 221 | if len(m.segments) < 1 { 222 | return fmt.Errorf("empty segment list") 223 | } 224 | 225 | // 1, write all the region/data to the binary file 226 | _, err := m.dstHandle.Seek(int64(HeaderInfoLength+VectorIndexLength), 0) 227 | if err != nil { 228 | return fmt.Errorf("seek to data first ptr: %w", err) 229 | } 230 | 231 | for _, seg := range m.segments { 232 | _, has := m.regionPool[seg.Region] 233 | if has { 234 | continue 235 | } 236 | 237 | var region = []byte(seg.Region) 238 | if len(region) > 0xFFFF { 239 | return fmt.Errorf("too long region info `%s`: should be less than %d bytes", seg.Region, 0xFFFF) 240 | } 241 | 242 | // get the first ptr of the next region 243 | pos, err := m.dstHandle.Seek(0, 1) 244 | if err != nil { 245 | return fmt.Errorf("seek to current ptr: %w", err) 246 | } 247 | 248 | _, err = m.dstHandle.Write(region) 249 | if err != nil { 250 | return fmt.Errorf("write region '%s': %w", seg.Region, err) 251 | } 252 | 253 | m.regionPool[seg.Region] = uint32(pos) 254 | } 255 | 256 | // 2, write the index block and cache the super index block 257 | var indexBuff = make([]byte, SegmentIndexSize) 258 | var counter, startIndexPtr, endIndexPtr = 0, int64(-1), int64(-1) 259 | for _, seg := range m.segments { 260 | dataPtr, has := m.regionPool[seg.Region] 261 | if !has { 262 | return fmt.Errorf("missing ptr cache for region `%s`", seg.Region) 263 | } 264 | 265 | // @Note: data length should be the length of bytes. 266 | // this works find cuz of the string feature (byte sequence) of golang. 267 | var dataLen = len(seg.Region) 268 | if dataLen < 1 { 269 | // @TODO: could this even be a case ? 270 | return fmt.Errorf("empty region info for segment '%s'", seg) 271 | } 272 | 273 | var segList = seg.Split() 274 | for _, s := range segList { 275 | pos, err := m.dstHandle.Seek(0, io.SeekCurrent) 276 | if err != nil { 277 | return fmt.Errorf("seek to segment index block: %w", err) 278 | } 279 | 280 | // encode the segment index 281 | binary.LittleEndian.PutUint32(indexBuff, s.StartIP) 282 | binary.LittleEndian.PutUint32(indexBuff[4:], s.EndIP) 283 | binary.LittleEndian.PutUint16(indexBuff[8:], uint16(dataLen)) 284 | binary.LittleEndian.PutUint32(indexBuff[10:], dataPtr) 285 | _, err = m.dstHandle.Write(indexBuff) 286 | if err != nil { 287 | return fmt.Errorf("write segment index for '%s': %w", s.String(), err) 288 | } 289 | 290 | m.setVectorIndex(s.StartIP, uint32(pos)) 291 | counter++ 292 | 293 | // check and record the start index ptr 294 | if startIndexPtr == -1 { 295 | startIndexPtr = pos 296 | } 297 | 298 | endIndexPtr = pos 299 | } 300 | } 301 | 302 | // synchronized the vector index block 303 | _, err = m.dstHandle.Seek(int64(HeaderInfoLength), 0) 304 | if err != nil { 305 | return fmt.Errorf("seek vector index first ptr: %w", err) 306 | } 307 | _, err = m.dstHandle.Write(m.vectorIndex) 308 | if err != nil { 309 | return fmt.Errorf("write vector index: %w", err) 310 | } 311 | 312 | // synchronized the segment index info 313 | binary.LittleEndian.PutUint32(indexBuff, uint32(startIndexPtr)) 314 | binary.LittleEndian.PutUint32(indexBuff[4:], uint32(endIndexPtr)) 315 | _, err = m.dstHandle.Seek(8, io.SeekStart) 316 | if err != nil { 317 | return fmt.Errorf("seek segment index ptr: %w", err) 318 | } 319 | 320 | _, err = m.dstHandle.Write(indexBuff[:8]) 321 | if err != nil { 322 | return fmt.Errorf("write segment index ptr: %w", err) 323 | } 324 | 325 | return nil 326 | } 327 | 328 | func (m *Maker) End() error { 329 | err := m.dstHandle.Close() 330 | if err != nil { 331 | return err 332 | } 333 | 334 | err = m.srcHandle.Close() 335 | if err != nil { 336 | return err 337 | } 338 | 339 | return nil 340 | } 341 | -------------------------------------------------------------------------------- /xdb_segment.go: -------------------------------------------------------------------------------- 1 | package Ip2regionTool 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type Segment struct { 9 | StartIP uint32 10 | EndIP uint32 11 | Region string 12 | } 13 | 14 | func SegmentFrom(seg string) (*Segment, error) { 15 | var ps = strings.SplitN(seg, "|", 3) 16 | if len(ps) != 3 { 17 | return nil, fmt.Errorf("invalid ip segment `%s`", seg) 18 | } 19 | 20 | sip, err := CheckIP(ps[0]) 21 | if err != nil { 22 | return nil, fmt.Errorf("check start ip `%s`: %s", ps[0], err) 23 | } 24 | 25 | eip, err := CheckIP(ps[1]) 26 | if err != nil { 27 | return nil, fmt.Errorf("check end ip `%s`: %s", ps[1], err) 28 | } 29 | 30 | if sip > eip { 31 | return nil, fmt.Errorf("start ip(%s) should not be greater than end ip(%s)", ps[0], ps[1]) 32 | } 33 | 34 | return &Segment{ 35 | StartIP: sip, 36 | EndIP: eip, 37 | Region: ps[2], 38 | }, nil 39 | } 40 | 41 | // Split the segment based on the pre-two bytes 42 | func (s *Segment) Split() []*Segment { 43 | // 1, split the segment with the first byte 44 | var tList []*Segment 45 | var sByte1, eByte1 = (s.StartIP >> 24) & 0xFF, (s.EndIP >> 24) & 0xFF 46 | var nSip = s.StartIP 47 | for i := sByte1; i <= eByte1; i++ { 48 | sip := (i << 24) | (nSip & 0xFFFFFF) 49 | eip := (i << 24) | 0xFFFFFF 50 | if eip < s.EndIP { 51 | nSip = (i + 1) << 24 52 | } else { 53 | eip = s.EndIP 54 | } 55 | 56 | // append the new segment (maybe) 57 | tList = append(tList, &Segment{ 58 | StartIP: sip, 59 | EndIP: eip, 60 | // @Note: don't bother to copy the region 61 | /// Region: s.Region, 62 | }) 63 | } 64 | 65 | // 2, split the segments with the second byte 66 | var segList []*Segment 67 | for _, seg := range tList { 68 | base := seg.StartIP & 0xFF000000 69 | nSip := seg.StartIP 70 | sb2, eb2 := (seg.StartIP>>16)&0xFF, (seg.EndIP>>16)&0xFF 71 | for i := sb2; i <= eb2; i++ { 72 | sip := base | (i << 16) | (nSip & 0xFFFF) 73 | eip := base | (i << 16) | 0xFFFF 74 | if eip < seg.EndIP { 75 | nSip = 0 76 | } else { 77 | eip = seg.EndIP 78 | } 79 | 80 | segList = append(segList, &Segment{ 81 | StartIP: sip, 82 | EndIP: eip, 83 | Region: s.Region, 84 | }) 85 | } 86 | } 87 | 88 | return segList 89 | } 90 | 91 | func (s *Segment) String() string { 92 | return Long2IP(s.StartIP) + "|" + Long2IP(s.EndIP) + "|" + s.Region 93 | } 94 | -------------------------------------------------------------------------------- /xdb_util.go: -------------------------------------------------------------------------------- 1 | package Ip2regionTool 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | var shiftIndex = []int{24, 16, 8, 0} 10 | 11 | func CheckIP(ip string) (uint32, error) { 12 | var ps = strings.Split(ip, ".") 13 | if len(ps) != 4 { 14 | return 0, fmt.Errorf("invalid ip address `%s`", ip) 15 | } 16 | 17 | var val uint32 18 | for i, s := range ps { 19 | d, err := strconv.Atoi(s) 20 | if err != nil { 21 | return 0, fmt.Errorf("the %dth part `%s` is not an integer", i, s) 22 | } 23 | 24 | if d < 0 || d > 255 { 25 | return 0, fmt.Errorf("the %dth part `%s` should be an integer bettween 0 and 255", i, s) 26 | } 27 | 28 | val |= uint32(d) << uint(shiftIndex[i]) 29 | } 30 | 31 | return val, nil 32 | } 33 | 34 | func Long2IP(ip uint32) string { 35 | return fmt.Sprintf("%d.%d.%d.%d", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, (ip>>0)&0xFF) 36 | } 37 | 38 | func MidIP(sip uint32, eip uint32) uint32 { 39 | return uint32((uint64(sip) + uint64(eip)) >> 1) 40 | } 41 | 42 | func CheckSegments(segList []*Segment) error { 43 | var last *Segment 44 | for _, seg := range segList { 45 | // sip must <= eip 46 | if seg.StartIP > seg.EndIP { 47 | return fmt.Errorf("segment `%s`: start ip should not be greater than end ip", seg.String()) 48 | } 49 | 50 | // check the continuity of the data segment 51 | if last != nil { 52 | if last.EndIP+1 != seg.StartIP { 53 | return fmt.Errorf("discontinuous segment `%s`: last.eip+1 != cur.sip", seg.String()) 54 | } 55 | } 56 | 57 | last = seg 58 | } 59 | 60 | return nil 61 | } 62 | --------------------------------------------------------------------------------