├── README.md ├── contracts ├── BytesToTypes.sol ├── Seriality.sol ├── SizeOf.sol ├── TypesToBytes.sol └── serialize.sol └── js └── deserialize.js /README.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | Thanks to [pouladzade](https://github.com/pouladzade) to make [Seriality](https://github.com/pouladzade/Seriality) public. 3 | 4 | # How to Use 5 | ```contracts``` folder contains solidity files to serialize a string array. 6 | ```js``` folder contains script to deserialize the byte output back to string array. 7 | You can follow [this](https://medium.com/@vaibhavsaini_67863/serializing-string-arrays-in-solidity-db4b6037e520) short medium article to get started. 8 | -------------------------------------------------------------------------------- /contracts/BytesToTypes.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | 3 | /** 4 | * @title BytesToTypes 5 | * @dev The BytesToTypes contract converts the memory byte arrays to the standard solidity types 6 | * @author pouladzade@gmail.com 7 | */ 8 | 9 | contract BytesToTypes { 10 | 11 | 12 | function bytesToAddress(uint _offst, bytes memory _input) internal pure returns (address _output) { 13 | 14 | assembly { 15 | _output := mload(add(_input, _offst)) 16 | } 17 | } 18 | 19 | function bytesToBool(uint _offst, bytes memory _input) internal pure returns (bool _output) { 20 | 21 | uint8 x; 22 | assembly { 23 | x := mload(add(_input, _offst)) 24 | } 25 | x==0 ? _output = false : _output = true; 26 | } 27 | 28 | function getStringSize(uint _offst, bytes memory _input) internal pure returns(uint size){ 29 | 30 | assembly{ 31 | 32 | size := mload(add(_input,_offst)) 33 | let chunk_count := add(div(size,32),1) // chunk_count = size/32 + 1 34 | 35 | if gt(mod(size,32),0) {// if size%32 > 0 36 | chunk_count := add(chunk_count,1) 37 | } 38 | 39 | size := mul(chunk_count,32)// first 32 bytes reseves for size in strings 40 | } 41 | } 42 | 43 | function bytesToString(uint _offst, bytes memory _input, bytes memory _output) internal { 44 | 45 | uint size = 32; 46 | assembly { 47 | let loop_index:= 0 48 | 49 | let chunk_count 50 | 51 | size := mload(add(_input,_offst)) 52 | chunk_count := add(div(size,32),1) // chunk_count = size/32 + 1 53 | 54 | if gt(mod(size,32),0) { 55 | chunk_count := add(chunk_count,1) // chunk_count++ 56 | } 57 | 58 | 59 | loop: 60 | mstore(add(_output,mul(loop_index,32)),mload(add(_input,_offst))) 61 | _offst := sub(_offst,32) // _offst -= 32 62 | loop_index := add(loop_index,1) 63 | 64 | jumpi(loop , lt(loop_index , chunk_count)) 65 | 66 | } 67 | } 68 | 69 | function bytesToBytes32(uint _offst, bytes memory _input, bytes32 _output) internal pure { 70 | 71 | assembly { 72 | mstore(_output , add(_input, _offst)) 73 | mstore(add(_output,32) , add(add(_input, _offst),32)) 74 | } 75 | } 76 | 77 | function bytesToInt8(uint _offst, bytes memory _input) internal pure returns (int8 _output) { 78 | 79 | assembly { 80 | _output := mload(add(_input, _offst)) 81 | } 82 | } 83 | 84 | function bytesToInt16(uint _offst, bytes memory _input) internal pure returns (int16 _output) { 85 | 86 | assembly { 87 | _output := mload(add(_input, _offst)) 88 | } 89 | } 90 | 91 | function bytesToInt24(uint _offst, bytes memory _input) internal pure returns (int24 _output) { 92 | 93 | assembly { 94 | _output := mload(add(_input, _offst)) 95 | } 96 | } 97 | 98 | function bytesToInt32(uint _offst, bytes memory _input) internal pure returns (int32 _output) { 99 | 100 | assembly { 101 | _output := mload(add(_input, _offst)) 102 | } 103 | } 104 | 105 | function bytesToInt40(uint _offst, bytes memory _input) internal pure returns (int40 _output) { 106 | 107 | assembly { 108 | _output := mload(add(_input, _offst)) 109 | } 110 | } 111 | 112 | function bytesToInt48(uint _offst, bytes memory _input) internal pure returns (int48 _output) { 113 | 114 | assembly { 115 | _output := mload(add(_input, _offst)) 116 | } 117 | } 118 | 119 | function bytesToInt56(uint _offst, bytes memory _input) internal pure returns (int56 _output) { 120 | 121 | assembly { 122 | _output := mload(add(_input, _offst)) 123 | } 124 | } 125 | 126 | function bytesToInt64(uint _offst, bytes memory _input) internal pure returns (int64 _output) { 127 | 128 | assembly { 129 | _output := mload(add(_input, _offst)) 130 | } 131 | } 132 | 133 | function bytesToInt72(uint _offst, bytes memory _input) internal pure returns (int72 _output) { 134 | 135 | assembly { 136 | _output := mload(add(_input, _offst)) 137 | } 138 | } 139 | 140 | function bytesToInt80(uint _offst, bytes memory _input) internal pure returns (int80 _output) { 141 | 142 | assembly { 143 | _output := mload(add(_input, _offst)) 144 | } 145 | } 146 | 147 | function bytesToInt88(uint _offst, bytes memory _input) internal pure returns (int88 _output) { 148 | 149 | assembly { 150 | _output := mload(add(_input, _offst)) 151 | } 152 | } 153 | 154 | function bytesToInt96(uint _offst, bytes memory _input) internal pure returns (int96 _output) { 155 | 156 | assembly { 157 | _output := mload(add(_input, _offst)) 158 | } 159 | } 160 | 161 | function bytesToInt104(uint _offst, bytes memory _input) internal pure returns (int104 _output) { 162 | 163 | assembly { 164 | _output := mload(add(_input, _offst)) 165 | } 166 | } 167 | 168 | function bytesToInt112(uint _offst, bytes memory _input) internal pure returns (int112 _output) { 169 | 170 | assembly { 171 | _output := mload(add(_input, _offst)) 172 | } 173 | } 174 | 175 | function bytesToInt120(uint _offst, bytes memory _input) internal pure returns (int120 _output) { 176 | 177 | assembly { 178 | _output := mload(add(_input, _offst)) 179 | } 180 | } 181 | 182 | function bytesToInt128(uint _offst, bytes memory _input) internal pure returns (int128 _output) { 183 | 184 | assembly { 185 | _output := mload(add(_input, _offst)) 186 | } 187 | } 188 | 189 | function bytesToInt136(uint _offst, bytes memory _input) internal pure returns (int136 _output) { 190 | 191 | assembly { 192 | _output := mload(add(_input, _offst)) 193 | } 194 | } 195 | 196 | function bytesToInt144(uint _offst, bytes memory _input) internal pure returns (int144 _output) { 197 | 198 | assembly { 199 | _output := mload(add(_input, _offst)) 200 | } 201 | } 202 | 203 | function bytesToInt152(uint _offst, bytes memory _input) internal pure returns (int152 _output) { 204 | 205 | assembly { 206 | _output := mload(add(_input, _offst)) 207 | } 208 | } 209 | 210 | function bytesToInt160(uint _offst, bytes memory _input) internal pure returns (int160 _output) { 211 | 212 | assembly { 213 | _output := mload(add(_input, _offst)) 214 | } 215 | } 216 | 217 | function bytesToInt168(uint _offst, bytes memory _input) internal pure returns (int168 _output) { 218 | 219 | assembly { 220 | _output := mload(add(_input, _offst)) 221 | } 222 | } 223 | 224 | function bytesToInt176(uint _offst, bytes memory _input) internal pure returns (int176 _output) { 225 | 226 | assembly { 227 | _output := mload(add(_input, _offst)) 228 | } 229 | } 230 | 231 | function bytesToInt184(uint _offst, bytes memory _input) internal pure returns (int184 _output) { 232 | 233 | assembly { 234 | _output := mload(add(_input, _offst)) 235 | } 236 | } 237 | 238 | function bytesToInt192(uint _offst, bytes memory _input) internal pure returns (int192 _output) { 239 | 240 | assembly { 241 | _output := mload(add(_input, _offst)) 242 | } 243 | } 244 | 245 | function bytesToInt200(uint _offst, bytes memory _input) internal pure returns (int200 _output) { 246 | 247 | assembly { 248 | _output := mload(add(_input, _offst)) 249 | } 250 | } 251 | 252 | function bytesToInt208(uint _offst, bytes memory _input) internal pure returns (int208 _output) { 253 | 254 | assembly { 255 | _output := mload(add(_input, _offst)) 256 | } 257 | } 258 | 259 | function bytesToInt216(uint _offst, bytes memory _input) internal pure returns (int216 _output) { 260 | 261 | assembly { 262 | _output := mload(add(_input, _offst)) 263 | } 264 | } 265 | 266 | function bytesToInt224(uint _offst, bytes memory _input) internal pure returns (int224 _output) { 267 | 268 | assembly { 269 | _output := mload(add(_input, _offst)) 270 | } 271 | } 272 | 273 | function bytesToInt232(uint _offst, bytes memory _input) internal pure returns (int232 _output) { 274 | 275 | assembly { 276 | _output := mload(add(_input, _offst)) 277 | } 278 | } 279 | 280 | function bytesToInt240(uint _offst, bytes memory _input) internal pure returns (int240 _output) { 281 | 282 | assembly { 283 | _output := mload(add(_input, _offst)) 284 | } 285 | } 286 | 287 | function bytesToInt248(uint _offst, bytes memory _input) internal pure returns (int248 _output) { 288 | 289 | assembly { 290 | _output := mload(add(_input, _offst)) 291 | } 292 | } 293 | 294 | function bytesToInt256(uint _offst, bytes memory _input) internal pure returns (int256 _output) { 295 | 296 | assembly { 297 | _output := mload(add(_input, _offst)) 298 | } 299 | } 300 | 301 | function bytesToUint8(uint _offst, bytes memory _input) internal pure returns (uint8 _output) { 302 | 303 | assembly { 304 | _output := mload(add(_input, _offst)) 305 | } 306 | } 307 | 308 | function bytesToUint16(uint _offst, bytes memory _input) internal pure returns (uint16 _output) { 309 | 310 | assembly { 311 | _output := mload(add(_input, _offst)) 312 | } 313 | } 314 | 315 | function bytesToUint24(uint _offst, bytes memory _input) internal pure returns (uint24 _output) { 316 | 317 | assembly { 318 | _output := mload(add(_input, _offst)) 319 | } 320 | } 321 | 322 | function bytesToUint32(uint _offst, bytes memory _input) internal pure returns (uint32 _output) { 323 | 324 | assembly { 325 | _output := mload(add(_input, _offst)) 326 | } 327 | } 328 | 329 | function bytesToUint40(uint _offst, bytes memory _input) internal pure returns (uint40 _output) { 330 | 331 | assembly { 332 | _output := mload(add(_input, _offst)) 333 | } 334 | } 335 | 336 | function bytesToUint48(uint _offst, bytes memory _input) internal pure returns (uint48 _output) { 337 | 338 | assembly { 339 | _output := mload(add(_input, _offst)) 340 | } 341 | } 342 | 343 | function bytesToUint56(uint _offst, bytes memory _input) internal pure returns (uint56 _output) { 344 | 345 | assembly { 346 | _output := mload(add(_input, _offst)) 347 | } 348 | } 349 | 350 | function bytesToUint64(uint _offst, bytes memory _input) internal pure returns (uint64 _output) { 351 | 352 | assembly { 353 | _output := mload(add(_input, _offst)) 354 | } 355 | } 356 | 357 | function bytesToUint72(uint _offst, bytes memory _input) internal pure returns (uint72 _output) { 358 | 359 | assembly { 360 | _output := mload(add(_input, _offst)) 361 | } 362 | } 363 | 364 | function bytesToUint80(uint _offst, bytes memory _input) internal pure returns (uint80 _output) { 365 | 366 | assembly { 367 | _output := mload(add(_input, _offst)) 368 | } 369 | } 370 | 371 | function bytesToUint88(uint _offst, bytes memory _input) internal pure returns (uint88 _output) { 372 | 373 | assembly { 374 | _output := mload(add(_input, _offst)) 375 | } 376 | } 377 | 378 | function bytesToUint96(uint _offst, bytes memory _input) internal pure returns (uint96 _output) { 379 | 380 | assembly { 381 | _output := mload(add(_input, _offst)) 382 | } 383 | } 384 | 385 | function bytesToUint104(uint _offst, bytes memory _input) internal pure returns (uint104 _output) { 386 | 387 | assembly { 388 | _output := mload(add(_input, _offst)) 389 | } 390 | } 391 | 392 | function bytesToUint112(uint _offst, bytes memory _input) internal pure returns (uint112 _output) { 393 | 394 | assembly { 395 | _output := mload(add(_input, _offst)) 396 | } 397 | } 398 | 399 | function bytesToUint120(uint _offst, bytes memory _input) internal pure returns (uint120 _output) { 400 | 401 | assembly { 402 | _output := mload(add(_input, _offst)) 403 | } 404 | } 405 | 406 | function bytesToUint128(uint _offst, bytes memory _input) internal pure returns (uint128 _output) { 407 | 408 | assembly { 409 | _output := mload(add(_input, _offst)) 410 | } 411 | } 412 | 413 | function bytesToUint136(uint _offst, bytes memory _input) internal pure returns (uint136 _output) { 414 | 415 | assembly { 416 | _output := mload(add(_input, _offst)) 417 | } 418 | } 419 | 420 | function bytesToUint144(uint _offst, bytes memory _input) internal pure returns (uint144 _output) { 421 | 422 | assembly { 423 | _output := mload(add(_input, _offst)) 424 | } 425 | } 426 | 427 | function bytesToUint152(uint _offst, bytes memory _input) internal pure returns (uint152 _output) { 428 | 429 | assembly { 430 | _output := mload(add(_input, _offst)) 431 | } 432 | } 433 | 434 | function bytesToUint160(uint _offst, bytes memory _input) internal pure returns (uint160 _output) { 435 | 436 | assembly { 437 | _output := mload(add(_input, _offst)) 438 | } 439 | } 440 | 441 | function bytesToUint168(uint _offst, bytes memory _input) internal pure returns (uint168 _output) { 442 | 443 | assembly { 444 | _output := mload(add(_input, _offst)) 445 | } 446 | } 447 | 448 | function bytesToUint176(uint _offst, bytes memory _input) internal pure returns (uint176 _output) { 449 | 450 | assembly { 451 | _output := mload(add(_input, _offst)) 452 | } 453 | } 454 | 455 | function bytesToUint184(uint _offst, bytes memory _input) internal pure returns (uint184 _output) { 456 | 457 | assembly { 458 | _output := mload(add(_input, _offst)) 459 | } 460 | } 461 | 462 | function bytesToUint192(uint _offst, bytes memory _input) internal pure returns (uint192 _output) { 463 | 464 | assembly { 465 | _output := mload(add(_input, _offst)) 466 | } 467 | } 468 | 469 | function bytesToUint200(uint _offst, bytes memory _input) internal pure returns (uint200 _output) { 470 | 471 | assembly { 472 | _output := mload(add(_input, _offst)) 473 | } 474 | } 475 | 476 | function bytesToUint208(uint _offst, bytes memory _input) internal pure returns (uint208 _output) { 477 | 478 | assembly { 479 | _output := mload(add(_input, _offst)) 480 | } 481 | } 482 | 483 | function bytesToUint216(uint _offst, bytes memory _input) internal pure returns (uint216 _output) { 484 | 485 | assembly { 486 | _output := mload(add(_input, _offst)) 487 | } 488 | } 489 | 490 | function bytesToUint224(uint _offst, bytes memory _input) internal pure returns (uint224 _output) { 491 | 492 | assembly { 493 | _output := mload(add(_input, _offst)) 494 | } 495 | } 496 | 497 | function bytesToUint232(uint _offst, bytes memory _input) internal pure returns (uint232 _output) { 498 | 499 | assembly { 500 | _output := mload(add(_input, _offst)) 501 | } 502 | } 503 | 504 | function bytesToUint240(uint _offst, bytes memory _input) internal pure returns (uint240 _output) { 505 | 506 | assembly { 507 | _output := mload(add(_input, _offst)) 508 | } 509 | } 510 | 511 | function bytesToUint248(uint _offst, bytes memory _input) internal pure returns (uint248 _output) { 512 | 513 | assembly { 514 | _output := mload(add(_input, _offst)) 515 | } 516 | } 517 | 518 | function bytesToUint256(uint _offst, bytes memory _input) internal pure returns (uint256 _output) { 519 | 520 | assembly { 521 | _output := mload(add(_input, _offst)) 522 | } 523 | } 524 | 525 | } 526 | -------------------------------------------------------------------------------- /contracts/Seriality.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | 3 | /** 4 | * @title Seriality 5 | * @dev The Seriality contract is the main interface for serializing data using the TypeToBytes, BytesToType and SizeOf 6 | * @author pouladzade@gmail.com 7 | */ 8 | 9 | import "./BytesToTypes.sol"; 10 | import "./TypesToBytes.sol"; 11 | import "./SizeOf.sol"; 12 | 13 | contract Seriality is BytesToTypes, TypesToBytes, SizeOf { 14 | 15 | function Seriality() public { 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /contracts/SizeOf.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | 3 | /** 4 | * @title SizeOf 5 | * @dev The SizeOf return the size of the solidity types in byte 6 | * @author pouladzade@gmail.com 7 | */ 8 | 9 | contract SizeOf { 10 | 11 | function sizeOfString(string _in) internal pure returns(uint _size){ 12 | _size = bytes(_in).length / 32; 13 | if(bytes(_in).length % 32 != 0) 14 | _size++; 15 | 16 | _size++; // first 32 bytes is reserved for the size of the string 17 | _size *= 32; 18 | } 19 | 20 | function sizeOfInt(uint16 _postfix) internal pure returns(uint size){ 21 | 22 | assembly{ 23 | switch _postfix 24 | case 8 { size := 1 } 25 | case 16 { size := 2 } 26 | case 24 { size := 3 } 27 | case 32 { size := 4 } 28 | case 40 { size := 5 } 29 | case 48 { size := 6 } 30 | case 56 { size := 7 } 31 | case 64 { size := 8 } 32 | case 72 { size := 9 } 33 | case 80 { size := 10 } 34 | case 88 { size := 11 } 35 | case 96 { size := 12 } 36 | case 104 { size := 13 } 37 | case 112 { size := 14 } 38 | case 120 { size := 15 } 39 | case 128 { size := 16 } 40 | case 136 { size := 17 } 41 | case 144 { size := 18 } 42 | case 152 { size := 19 } 43 | case 160 { size := 20 } 44 | case 168 { size := 21 } 45 | case 176 { size := 22 } 46 | case 184 { size := 23 } 47 | case 192 { size := 24 } 48 | case 200 { size := 25 } 49 | case 208 { size := 26 } 50 | case 216 { size := 27 } 51 | case 224 { size := 28 } 52 | case 232 { size := 29 } 53 | case 240 { size := 30 } 54 | case 248 { size := 31 } 55 | case 256 { size := 32 } 56 | default { size := 32 } 57 | } 58 | 59 | } 60 | 61 | function sizeOfUint(uint16 _postfix) internal pure returns(uint size){ 62 | return sizeOfInt(_postfix); 63 | } 64 | 65 | function sizeOfAddress() internal pure returns(uint8){ 66 | return 20; 67 | } 68 | 69 | function sizeOfBool() internal pure returns(uint8){ 70 | return 1; 71 | } 72 | 73 | 74 | } -------------------------------------------------------------------------------- /contracts/TypesToBytes.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | 3 | /** 4 | * @title TypesToBytes 5 | * @dev The TypesToBytes contract converts the standard solidity types to the byte array 6 | * @author pouladzade@gmail.com 7 | */ 8 | 9 | contract TypesToBytes { 10 | 11 | function TypesToBytes() internal { 12 | 13 | } 14 | function addressToBytes(uint _offst, address _input, bytes memory _output) internal pure { 15 | 16 | assembly { 17 | mstore(add(_output, _offst), _input) 18 | } 19 | } 20 | 21 | function bytes32ToBytes(uint _offst, bytes32 _input, bytes memory _output) internal pure { 22 | 23 | assembly { 24 | mstore(add(_output, _offst), _input) 25 | mstore(add(add(_output, _offst),32), add(_input,32)) 26 | } 27 | } 28 | 29 | function boolToBytes(uint _offst, bool _input, bytes memory _output) internal pure { 30 | uint8 x = _input == false ? 0 : 1; 31 | assembly { 32 | mstore(add(_output, _offst), x) 33 | } 34 | } 35 | 36 | function stringToBytes(uint _offst, bytes memory _input, bytes memory _output) internal { 37 | uint256 stack_size = _input.length / 32; 38 | if(_input.length % 32 > 0) stack_size++; 39 | 40 | assembly { 41 | let index := 0 42 | stack_size := add(stack_size,1)//adding because of 32 first bytes memory as the length 43 | loop: 44 | 45 | mstore(add(_output, _offst), mload(add(_input,mul(index,32)))) 46 | _offst := sub(_offst , 32) 47 | index := add(index ,1) 48 | jumpi(loop , lt(index,stack_size)) 49 | } 50 | } 51 | 52 | function intToBytes(uint _offst, int _input, bytes memory _output) internal pure { 53 | 54 | assembly { 55 | mstore(add(_output, _offst), _input) 56 | } 57 | } 58 | 59 | function uintToBytes(uint _offst, uint _input, bytes memory _output) internal pure { 60 | 61 | assembly { 62 | mstore(add(_output, _offst), _input) 63 | } 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /contracts/serialize.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./Seriality.sol"; 4 | 5 | contract Serialize is Seriality{ 6 | string [] arr = ["string0", "string1", "string2", "string3", "string4"]; 7 | 8 | function getBytes(uint startindex, uint endindex) public view returns(bytes serialized){ 9 | 10 | require(endindex >= startindex); 11 | 12 | if(endindex > (arr.length - 1)){ 13 | endindex = arr.length - 1; 14 | } 15 | 16 | //64 byte is needed for safe storage of a single string. 17 | //((endindex - startindex) + 1) is the number of strings we want to pull out. 18 | uint offset = 64*((endindex - startindex) + 1); 19 | 20 | bytes memory buffer = new bytes(offset); 21 | string memory out1 = new string(32); 22 | 23 | 24 | for(uint i = endindex; i >= startindex; i--){ 25 | out1 = arr[i]; 26 | 27 | stringToBytes(offset, bytes(out1), buffer); 28 | offset -= sizeOfString(out1); 29 | } 30 | 31 | return (buffer); 32 | } 33 | 34 | function getString(bytes buffer) public view returns(string string1, string string2){ 35 | 36 | //64 byte is needed for safe storage of a single string. 37 | //In this example we are returning 2 strings 38 | uint offset = 64*2; 39 | 40 | buffer = new bytes(offset); 41 | 42 | string1 = new string(32); 43 | string2 = new string(32); 44 | 45 | bytesToString(offset, buffer, bytes(string2)); 46 | offset -= sizeOfString(string2); 47 | 48 | bytesToString(offset, buffer, bytes(string2)); 49 | offset -= sizeOfString(string2); 50 | 51 | return(string1, string2); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /js/deserialize.js: -------------------------------------------------------------------------------- 1 | var hexStringFromSolidity = "" 2 | 3 | var stringArray = convert(hexStringFromSolidity); 4 | 5 | 6 | var convert = function hexToStr(hex) { 7 | var str = ''; 8 | for (var i = 0; i < hex.length; i += 2) { 9 | var v = parseInt(hex.substr(i, 2), 16); 10 | if (v) str += String.fromCharCode(v); 11 | } 12 | 13 | params = []; 14 | res = ""; 15 | for (var i=0; i<= str.length; i++){ 16 | if(str.charCodeAt(i) > 31){ 17 | res = res + str[i]; 18 | } 19 | else{ 20 | params.push(res); 21 | res = ""; 22 | } 23 | } 24 | params.pop(); 25 | 26 | return params; 27 | } 28 | --------------------------------------------------------------------------------