2025-02-24 19:22:01 +03:00
|
|
|
<?php
|
|
|
|
namespace taskman;
|
|
|
|
use Exception;
|
|
|
|
|
|
|
|
class ConfigPackParams
|
|
|
|
{
|
|
|
|
/*var ConfigCacheEntry[]*/
|
|
|
|
public array $cache_entries;
|
|
|
|
public bool $use_lz4 = false;
|
|
|
|
public bool $use_config_refs = false;
|
|
|
|
public int $binary_format = 1; //1,2 supported
|
|
|
|
public ?int $version = null;
|
|
|
|
public bool $debug = false;
|
|
|
|
|
|
|
|
function __construct(array $cache_entries, int $version, bool $use_lz4 = false,
|
|
|
|
bool $use_config_refs = false, int $binary_format = 1, bool $debug = false)
|
|
|
|
{
|
|
|
|
$this->cache_entries = $cache_entries;
|
|
|
|
$this->use_lz4 = $use_lz4;
|
|
|
|
$this->use_config_refs = $use_config_refs;
|
|
|
|
$this->binary_format = $binary_format;
|
|
|
|
$this->version = $version;
|
|
|
|
$this->debug = $debug;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function config_pack_bundle(ConfigPackParams $params) : string
|
|
|
|
{
|
|
|
|
$t = microtime(true);
|
|
|
|
|
|
|
|
$packed_data = null;
|
|
|
|
|
|
|
|
if($params->binary_format == 1)
|
|
|
|
{
|
|
|
|
$packed_data = _config_pack_bundle_fmt1(
|
|
|
|
$params->cache_entries,
|
|
|
|
$params->use_lz4,
|
|
|
|
$params->use_config_refs,
|
|
|
|
$params->version
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else if($params->binary_format == 2)
|
|
|
|
{
|
|
|
|
$packed_data = _config_pack_bundle_fmt2(
|
|
|
|
$params->cache_entries,
|
|
|
|
$params->use_lz4,
|
|
|
|
$params->use_config_refs,
|
|
|
|
$params->version
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw new Exception("Unknown binary format: {$params->binary_format}");
|
|
|
|
|
|
|
|
if($params->debug)
|
2025-02-25 15:48:28 +03:00
|
|
|
config_log("Packed entries: " . sizeof($params->cache_entries) . ", total: " .
|
2025-02-24 19:22:01 +03:00
|
|
|
kb($packed_data) . ", format: {$params->binary_format}, lz4: " .
|
|
|
|
var_export($params->use_lz4, true) . ", refs: " . var_export($params->use_config_refs, true) .
|
|
|
|
", CRC: " . crc32($packed_data) .
|
2025-02-25 15:48:28 +03:00
|
|
|
", " . round(microtime(true) - $t,2) . " sec.");
|
2025-02-24 19:22:01 +03:00
|
|
|
|
|
|
|
return $packed_data;
|
|
|
|
}
|
|
|
|
|
2025-02-27 01:35:16 +03:00
|
|
|
//NOTE: strids are stored as CRCs, potential collision may happen (error will be raised during build)
|
2025-02-24 19:22:01 +03:00
|
|
|
function _config_pack_bundle_fmt1(
|
|
|
|
array $cache_entries,
|
|
|
|
bool $use_lz4,
|
|
|
|
bool $use_config_refs,
|
|
|
|
int $version) : string
|
|
|
|
{
|
|
|
|
$MAP = array();
|
|
|
|
$STRIDMAP = array();
|
|
|
|
|
|
|
|
$payloads = array();
|
|
|
|
$payloads_offset = 0;
|
|
|
|
foreach($cache_entries as $entry)
|
|
|
|
{
|
|
|
|
list($format, $payload) = _config_get_payload($entry, $use_lz4, $use_config_refs);
|
|
|
|
$payload_size = strlen($payload);
|
|
|
|
$payloads[] = array($payloads_offset, $payload, $format, $payload_size);
|
|
|
|
$payloads_offset += $payload_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
$header = array();
|
|
|
|
foreach($cache_entries as $idx => $entry)
|
|
|
|
{
|
|
|
|
if(isset($MAP[$entry->id]))
|
|
|
|
throw new Exception("Duplicating config id for '{$entry->strid}' conflicts with '{$MAP[$entry->id]}'");
|
|
|
|
$MAP[$entry->id] = $entry->strid;
|
|
|
|
|
|
|
|
$strid_crc = crc32($entry->strid);
|
|
|
|
if(isset($STRIDMAP[$strid_crc]))
|
|
|
|
throw new Exception("Duplicating config str id crc for '{$entry->strid}' conflicts with '{$STRIDMAP[$strid_crc]}'");
|
|
|
|
$STRIDMAP[$strid_crc] = $entry->strid;
|
|
|
|
|
|
|
|
$header[] = array(
|
|
|
|
$payloads[$idx][2],
|
|
|
|
$entry->id,
|
|
|
|
crc32($entry->strid),
|
|
|
|
$entry->class_id,
|
|
|
|
$payloads[$idx][0],
|
|
|
|
$payloads[$idx][3]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$header_msgpack = config_msgpack_pack($header);
|
|
|
|
$payloads_bundle = '';
|
|
|
|
foreach($payloads as $item)
|
|
|
|
$payloads_bundle .= $item[1];
|
|
|
|
|
|
|
|
$packed_data =
|
|
|
|
pack("C", 1) .
|
|
|
|
pack("V", $version) .
|
|
|
|
pack("V", strlen($header_msgpack)) .
|
|
|
|
$header_msgpack .
|
|
|
|
$payloads_bundle;
|
|
|
|
|
|
|
|
return $packed_data;
|
|
|
|
}
|
|
|
|
|
2025-02-27 01:35:16 +03:00
|
|
|
//NOTE: strids are stored as lookup strings
|
2025-02-24 19:22:01 +03:00
|
|
|
function _config_pack_bundle_fmt2(
|
|
|
|
array $cache_entries,
|
|
|
|
bool $use_lz4,
|
|
|
|
bool $use_config_refs,
|
|
|
|
int $version) : string
|
|
|
|
{
|
|
|
|
$MAP = array();
|
|
|
|
$STRIDMAP = array();
|
|
|
|
$STRIDLIST = array();
|
|
|
|
|
|
|
|
$payloads = array();
|
|
|
|
$strids = array();
|
|
|
|
$payloads_offset = 0;
|
|
|
|
foreach($cache_entries as $entry)
|
|
|
|
{
|
|
|
|
list($format, $payload) = _config_get_payload($entry, $use_lz4, $use_config_refs);
|
|
|
|
$payload_size = strlen($payload);
|
|
|
|
$payloads[] = array($payloads_offset, $payload, $format, $payload_size);
|
|
|
|
$payloads_offset += $payload_size;
|
|
|
|
|
|
|
|
$strids_indices = array();
|
|
|
|
$strid_parts = explode('/', ltrim($entry->strid, '@'));
|
|
|
|
foreach($strid_parts as $strid_part)
|
|
|
|
{
|
|
|
|
if(!isset($STRIDMAP[$strid_part]))
|
|
|
|
{
|
|
|
|
$strid_index = count($STRIDLIST);
|
|
|
|
$STRIDLIST[] = $strid_part;
|
|
|
|
$STRIDMAP[$strid_part] = $strid_index;
|
|
|
|
$strids_indices[] = $strid_index;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
$strids_indices[] = $STRIDMAP[$strid_part];
|
|
|
|
}
|
|
|
|
$strids[] = $strids_indices;
|
|
|
|
}
|
|
|
|
|
|
|
|
$header = array();
|
|
|
|
foreach($cache_entries as $idx => $entry)
|
|
|
|
{
|
|
|
|
if(isset($MAP[$entry->id]))
|
|
|
|
throw new Exception("Duplicating config id for '{$entry->strid}' conflicts with '{$MAP[$entry->id]}'");
|
|
|
|
$MAP[$entry->id] = $entry->strid;
|
|
|
|
|
|
|
|
$header[] = array(
|
|
|
|
$payloads[$idx][2], //format
|
|
|
|
$entry->id,
|
|
|
|
$strids[$idx], //strid as a lookup indices
|
|
|
|
$entry->class_id,
|
|
|
|
$payloads[$idx][0], //offset
|
|
|
|
$payloads[$idx][3] //size
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$strids_msgpack = config_msgpack_pack($STRIDLIST);
|
|
|
|
|
|
|
|
$header_msgpack = config_msgpack_pack($header);
|
|
|
|
$payloads_bundle = '';
|
|
|
|
foreach($payloads as $item)
|
|
|
|
$payloads_bundle .= $item[1];
|
|
|
|
|
|
|
|
$packed_data =
|
|
|
|
pack("C", 2) .
|
|
|
|
pack("V", $version) .
|
|
|
|
pack("V", strlen($strids_msgpack)) .
|
|
|
|
pack("V", strlen($header_msgpack)) .
|
|
|
|
$strids_msgpack .
|
|
|
|
$header_msgpack .
|
|
|
|
$payloads_bundle;
|
|
|
|
|
|
|
|
return $packed_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
//format: [[class_id, [data]], ...[class_id, [data]]]
|
|
|
|
function config_unpack_bundle(string $packed_data) : array
|
|
|
|
{
|
|
|
|
$packed_info = substr($packed_data, 0, 1);
|
|
|
|
$info = unpack('Cformat', $packed_info);
|
|
|
|
|
|
|
|
if($info['format'] === 1)
|
|
|
|
{
|
|
|
|
return _config_unpack_bundle_fmt1($packed_data);
|
|
|
|
}
|
|
|
|
else if($info['format'] === 2)
|
|
|
|
{
|
|
|
|
return _config_unpack_bundle_fmt2($packed_data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw new Exception("Unknown format: {$info['format']}");
|
|
|
|
}
|
|
|
|
|
|
|
|
function _config_unpack_bundle_fmt1(string $packed_data) : array
|
|
|
|
{
|
|
|
|
$packed_info = substr($packed_data, 0, 1+4+4);
|
|
|
|
|
|
|
|
$info = unpack('Cformat/Vversion/Vheader_len', $packed_info);
|
|
|
|
|
|
|
|
if($info['format'] !== 1)
|
|
|
|
throw new Exception("Unknown format: {$info['format']}");
|
|
|
|
|
|
|
|
$header_msgpack = substr($packed_data, 1+4+4, $info['header_len']);
|
|
|
|
$header = config_msgpack_unpack($header_msgpack);
|
|
|
|
|
|
|
|
$payloads_bundle = substr($packed_data, 1+4+4+$info['header_len']);
|
|
|
|
|
|
|
|
$entries = array();
|
|
|
|
foreach($header as $item)
|
|
|
|
{
|
|
|
|
list($format, $id, $strid_crc, $class_id, $offset, $size) = $item;
|
|
|
|
|
|
|
|
$payload = substr($payloads_bundle, $offset, $size);
|
|
|
|
|
|
|
|
$entries[$id] = array($class_id, _config_unpack_payload($format, $payload));
|
|
|
|
}
|
|
|
|
|
|
|
|
return $entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _config_unpack_bundle_fmt2(string $packed_data) : array
|
|
|
|
{
|
|
|
|
$packed_info = substr($packed_data, 0, 1+4+4+4);
|
|
|
|
|
|
|
|
$info = unpack('Cformat/Vversion/Vstrids_len/Vheader_len', $packed_info);
|
|
|
|
|
|
|
|
if($info['format'] !== 2)
|
|
|
|
throw new Exception("Unknown format: {$info['format']}");
|
|
|
|
|
|
|
|
$strids_msgpack = substr($packed_data, 1+4+4+4, $info['strids_len']);
|
|
|
|
$strids = config_msgpack_unpack($strids_msgpack);
|
|
|
|
|
|
|
|
$header_msgpack = substr($packed_data, 1+4+4+4+$info['strids_len'], $info['header_len']);
|
|
|
|
$header = config_msgpack_unpack($header_msgpack);
|
|
|
|
|
|
|
|
$payloads_bundle = substr($packed_data, 1+4+4+4+$info['strids_len']+$info['header_len']);
|
|
|
|
|
|
|
|
$entries = array();
|
|
|
|
foreach($header as $item)
|
|
|
|
{
|
|
|
|
list($format, $id, $strid_crc, $class_id, $offset, $size) = $item;
|
|
|
|
|
|
|
|
$payload = substr($payloads_bundle, $offset, $size);
|
|
|
|
|
|
|
|
$entries[$id] = array($class_id, _config_unpack_payload($format, $payload));
|
|
|
|
}
|
|
|
|
|
|
|
|
return $entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
//format: [format_id, payload_data]
|
|
|
|
function _config_get_payload(ConfigCacheEntry $ce, bool $use_lz4, bool $use_config_refs) : array
|
|
|
|
{
|
|
|
|
$format = ConfigCacheEntry::FMT_BINARY;
|
|
|
|
$payload = null;
|
|
|
|
|
|
|
|
if($use_config_refs && $ce->payload_file)
|
|
|
|
{
|
|
|
|
$format = ConfigCacheEntry::FMT_FILE_REF;
|
|
|
|
$payload = $ce->payload_file;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$payload = $ce->payload;
|
|
|
|
if($use_lz4 && strlen($payload) > 512)
|
|
|
|
{
|
|
|
|
$format = ConfigCacheEntry::FMT_LZ4;
|
|
|
|
$payload = lz4_compress($payload, 9);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array($format, $payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
function _config_unpack_payload(int $format, string $payload) : array
|
|
|
|
{
|
|
|
|
$msg_packed = null;
|
|
|
|
if($format === ConfigCacheEntry::FMT_LZ4)
|
|
|
|
$msg_packed = lz4_uncompress($payload);
|
|
|
|
else if($format === ConfigCacheEntry::FMT_BINARY)
|
|
|
|
$msg_packed = $payload;
|
|
|
|
else if($format === ConfigCacheEntry::FMT_FILE_REF)
|
|
|
|
$msg_packed = ensure_read($payload);
|
|
|
|
else
|
|
|
|
throw new Exception("Bad format: $format");
|
|
|
|
return config_msgpack_unpack($msg_packed);
|
|
|
|
}
|
|
|
|
|
|
|
|
function config_pack_and_write_bundle(
|
|
|
|
/*var ConfBase[]*/
|
|
|
|
array $configs,
|
|
|
|
string $file_path,
|
2025-02-26 18:44:36 +03:00
|
|
|
int $version,
|
2025-02-24 19:22:01 +03:00
|
|
|
bool $use_lz4 = true,
|
|
|
|
int $binary_format = 1,
|
2025-02-27 13:16:45 +03:00
|
|
|
bool $debug = false
|
2025-02-24 19:22:01 +03:00
|
|
|
)
|
|
|
|
{
|
|
|
|
$cache_entries = array();
|
|
|
|
foreach($configs as $conf)
|
|
|
|
{
|
|
|
|
$payload = config_msgpack_pack($conf->export());
|
|
|
|
|
|
|
|
//creating fake cache entries
|
|
|
|
$entry = new ConfigCacheEntry();
|
|
|
|
$entry->id = $conf->id;
|
|
|
|
$entry->strid = $conf->strid;
|
|
|
|
$entry->class_id = $conf->getClassId();
|
|
|
|
$entry->class = get_class($conf);
|
|
|
|
$entry->payload = $payload;
|
|
|
|
$entry->config = $conf;
|
|
|
|
|
|
|
|
$cache_entries[] = $entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
$packed_data = config_pack_bundle(
|
|
|
|
new ConfigPackParams(
|
|
|
|
cache_entries: $cache_entries,
|
|
|
|
use_lz4: $use_lz4,
|
|
|
|
binary_format: $binary_format,
|
2025-02-27 13:16:45 +03:00
|
|
|
version: $version,
|
|
|
|
debug: $debug
|
2025-02-24 19:22:01 +03:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
ensure_write($file_path, $packed_data);
|
|
|
|
}
|
|
|
|
|