import { mapClass } from './libvbar-m-dxmap.so' /** * In-memory Key-Value Store Module (dxMap) * * Features: * - Provides a topic-based, in-memory key-value storage. * - Thread-safe: Data can be safely accessed and modified from multiple JavaScript threads. * - Supports storing various data types (string, number, boolean, object, array) by automatically serializing/deserializing them. * * Usage: * - First, get a map instance for a specific topic using `map.get('myTopic')`. * - Then, use the instance's methods (`put`, `get`, `has`, `del`, etc.) to manage data within that topic. * * @example * import map from 'dxMap'; * * // Get an instance for the 'user' topic * const userMap = map.get('user'); * * // Put some data * userMap.put('name', 'John Doe'); * userMap.put('age', 30); * userMap.put('isActive', true); * userMap.put('roles', ['admin', 'editor']); * * // Get data * const name = userMap.get('name'); // "John Doe" * * // Check if a key exists * const hasAge = userMap.has('age'); // true * * // Delete a key * userMap.del('isActive'); * * // Get all keys for the topic * const allKeys = userMap.keys(); // ['name', 'age', 'roles'] * * Doc/Demo: https://github.com/DejaOS/DejaOS */ const mapObj = new mapClass(); const map = { /** * Gets a map instance for a given topic name. * Each topic is a separate namespace for keys. * @param {string} name - The name of the topic. Must not be null or empty. * @returns {{keys: (function(): string[]), get: (function(string): *), has: (function(string): boolean), put: (function(string, *): boolean), del: (function(string): boolean), destroy: (function(): boolean)}} An object with methods to interact with the map for the specified topic. * @throws {Error} If the name is null or empty. */ get: function (name) { if (!name || name.length == 0) { throw new Error("dxMap.get:name should not be null or empty") } //第一次put会自动创建实例 return { /** * Retrieves all keys within the current topic. * @returns {string[]} An array of all keys for the topic. Returns an empty array if the topic is empty or does not exist. */ keys: function () { let all = mapObj.keys(name) return all == null ? [] : all }, /** * Retrieves the value associated with a key within the current topic. * The returned value will be deserialized to its original type (number, boolean, object, array, or string). * @param {string} key - The key to retrieve. Must not be null or empty. * @returns {*} The value associated with the key, or `undefined` if the key does not exist. * @throws {Error} If the key is null or empty. */ get: function (key) { if (!key || key.length < 1) { throw new Error("The 'key' parameter cannot be null or empty") } // put空字符串,get会是null let value = mapObj.get(name, key) // C layer returns null if not found. JS layer should propagate this. if (value === null) { return undefined; // Return undefined for non-existent keys, a common JS pattern. } return _parseString(value) }, /** * Checks if a key exists within the current topic. * @param {string} key - The key to check. Must not be null or empty. * @returns {boolean} `true` if the key exists, `false` otherwise. * @throws {Error} If the key is null or empty. */ has: function (key) { if (!key || key.length < 1) { throw new Error("The 'key' parameter cannot be null or empty") } return mapObj.has(name, key) }, /** * Inserts or updates a key-value pair within the current topic. * The value will be automatically serialized. * If `value` is `null` or `undefined`, the key will be deleted. * @param {string} key - The key to set. Must not be null or empty. * @param {*} value - The value to associate with the key. Supported types: string, number, boolean, object, array. Functions are not supported. * @returns {boolean} Returns `true` on success. * @throws {Error} If the key is null or empty, or if the value is a function. */ put: function (key, value) { if (!key || key.length < 1) { throw new Error("The 'key' parameter cannot be null or empty") } // Implement "set null/undefined to delete" logic. if (value === null || value === undefined) { return mapObj.delete(name, key); } return mapObj.insert(name, key, _stringifyValue(value)) }, /** * Deletes a key-value pair from the current topic. * @param {string} key - The key to delete. Must not be null or empty. * @returns {boolean} `true` if the key was found and deleted, `false` otherwise. * @throws {Error} If the key is null or empty. */ del: function (key) { if (!key || key.length < 1) { throw new Error("The 'key' parameter cannot be null or empty") } return mapObj.delete(name, key) }, /** * Destroys the entire topic, deleting all its keys and freeing associated memory. * After calling destroy, the instance should not be used anymore. * @returns {boolean} Returns `true` on success. */ destroy: function () { return mapObj.destroy(name) }, } } } /** * Serializes a value into a string with a type prefix. * @private * @param {*} value - The value to serialize. * @returns {string} The serialized string. */ function _stringifyValue(value) { const type = typeof value if (type === 'string') { return value } if (type === 'number') { return '#n#' + value } if (type === 'boolean') { return '#b#' + value } if (type === 'object') { // 如果是对象,进一步判断是否为数组 if (Array.isArray(value)) { return '#a#' + JSON.stringify(value); }// else if (value === null) { 前面已经规避了null的情况 return '#o#' + JSON.stringify(value) } if (type === 'function') { throw new Error("The 'value' parameter should not be function") } } /** * Deserializes a string with a type prefix back to its original value. * @private * @param {string} str - The string to deserialize. * @returns {*} The deserialized value. */ function _parseString(str) { if (str.startsWith('#n#')) { // 解析数字 const numberStr = str.substring(3); return numberStr.includes('.') ? parseFloat(numberStr) : parseInt(numberStr, 10); } else if (str.startsWith('#b#')) { // 解析布尔值 return str.substring(3) === 'true'; } else if (str.startsWith('#a#')) { // 解析数组 return JSON.parse(str.substring(3)); } else if (str.startsWith('#o#')) { // 解析对象 return JSON.parse(str.substring(3)); } else { // 默认情况下,将字符串返回 return str; } } export default map;