'; } // compare two ip bytes (packed string return by parsedIP) // returns: -1 if ip1 < ip2, 0 if ip1 == ip2 or 1 if ip1 > ip2 public static function ipSubCompare($ip1, $buff, $offset) { // $r = substr_compare($ip1, $buff, $offset, strlen($ip1)); // @Note: substr_compare is not working, use the substr + strcmp instead $r = strcmp($ip1, substr($buff, $offset, strlen($ip1))); if ($r < 0) { return -1; } else if ($r > 0) { return 1; } else { return 0; } } // returns: -1 if ip1 < ip2, 0 if ip1 == ip2 or 1 if ip1 > ip2 public static function ipCompare($ip1, $ip2) { $r = strcmp($ip1, $ip2); if ($r < 0) { return -1; } else if ($r > 0) { return 1; } else { return 0; } } // version parse public static function versionFromName($ver_name) { $name = strtoupper($ver_name); if ($name == "V4" || $name == "IPv4") { return IPv4::default(); } else if ($name == "V6" || $name == "IPv6") { return IPv6::default(); } else { throw new Exception("invalid verstion name `{$ver_name}`"); } } // version parse from header public static function versionFromHeader($header) { // Old structure 2.0 with IPv4 supports ONLY if ($header['version'] == Structure_20) { return IPv4::default(); } // structure 3.0 after IPv6 supporting if ($header['version'] != Structure_30) { throw new Exception("invalid xdb structure version `{$header['version']}`"); } if ($header['ipVersion'] == IPv4VersionNo) { return IPv4::default(); } else if ($header['ipVersion'] == IPv6VersionNo) { return IPv6::default(); } else { throw new Exception("invalid ip version number `{$header['ipVersion']}`"); } } // binary string chars implode with space public static function bytesToString($buff, $offset, $length) { $sb = []; for ($i = 0; $i < $length; $i++) { $sb[] = ord($buff[$offset+$i]) & 0xFF; } return '['.implode(' ', $sb).']'; } // decode a 4bytes long with Little endian byte order from a byte buffer public static function le_getUint32($b, $idx) { $val = (ord($b[$idx])) | (ord($b[$idx+1]) << 8) | (ord($b[$idx+2]) << 16) | (ord($b[$idx+3]) << 24); // convert signed int to unsigned int if on 32 bit operating system if ($val < 0 && PHP_INT_SIZE == 4) { $val = sprintf("%u", $val); } return $val; } // read a 2bytes int with litten endian byte order from a byte buffer public static function le_getUint16($b, $idx) { return ((ord($b[$idx])) | (ord($b[$idx+1]) << 8)); } // Verify if the current Searcher could be used to search the specified xdb file. // Why do we need this check ? // The future features of the xdb impl may cause the current searcher not able to work properly. // // @Note: You Just need to check this ONCE when the service starts // Or use another process (eg, A command) to check once Just to confirm the suitability. // returns: null for everything is ok or the error string. public static function verify($handle) { // load the header $header = self::loadHeader($handle); if ($header == null) { return 'failed to load the header'; } // get the runtime ptr bytes $runtimePtrBytes = 0; if ($header['version'] == Structure_20) { $runtimePtrBytes = 4; } else if ($header['version'] == Structure_30) { $runtimePtrBytes = $header['runtimePtrBytes']; } else { return "invalid structure version `{$header['version']}`"; } // 1, confirm the xdb file size // to ensure that the maximum file pointer does not overflow $stat = fstat($handle); if ($stat == false) { return 'failed to stat the xdb file'; } $maxFilePtr = (1 << ($runtimePtrBytes * 8)) - 1; // print_r([$stat['size'], $maxFilePtr]); if ($stat['size'] > $maxFilePtr) { return "xdb file exceeds the maximum supported bytes: {$maxFilePtr}"; } return null; } public static function verifyFromFile($dbFile) { $handle = fopen($dbFile, 'r'); if ($handle === false) { return null; } $r = self::verify($handle); fclose($handle); return $r; } // load header info from a specified file handle public static function loadHeader($handle) { if (fseek($handle, 0) == -1) { return null; } $buff = fread($handle, HeaderInfoLength); if ($buff === false) { return null; } // read bytes length checking if (strlen($buff) != HeaderInfoLength) { return null; } // return the decoded header info return array( 'version' => self::le_getUint16($buff, 0), 'indexPolicy' => self::le_getUint16($buff, 2), 'createdAt' => self::le_getUint32($buff, 4), 'startIndexPtr' => self::le_getUint32($buff, 8), 'endIndexPtr' => self::le_getUint32($buff, 12), 'ipVersion' => self::le_getUint16($buff, 16), 'runtimePtrBytes' => self::le_getUint16($buff, 18) ); } // load header info from the specified xdb file path public static function loadHeaderFromFile($dbFile) { $handle = fopen($dbFile, 'r'); if ($handle === false) { return null; } $header = self::loadHeader($handle); fclose($handle); return $header; } // load vector index from a file handle public static function loadVectorIndex($handle) { if (fseek($handle, HeaderInfoLength) == -1) { return null; } $rLen = VectorIndexRows * VectorIndexCols * VectorIndexSize; $buff = fread($handle, $rLen); if ($buff === false) { return null; } if (strlen($buff) != $rLen) { return null; } return $buff; } // load vector index from a specified xdb file path public static function loadVectorIndexFromFile($dbFile) { $handle = fopen($dbFile, 'r'); if ($handle === false) { return null; } $vIndex = self::loadVectorIndex($handle); fclose($handle); return $vIndex; } // load the xdb content from a file handle public static function loadContent($handle) { if (fseek($handle, 0, SEEK_END) == -1) { return null; } $size = ftell($handle); if ($size === false) { return null; } // seek to the head for reading if (fseek($handle, 0) == -1) { return null; } $buff = fread($handle, $size); if ($buff === false) { return null; } // read length checking if (strlen($buff) != $size) { return null; } return $buff; } // load the xdb content from a file path public static function loadContentFromFile($dbFile) { $str = file_get_contents($dbFile, false); if ($str === false) { return null; } else { return $str; } } public static function now() { return (microtime(true) * 1000); } }