Skip to content

Instantly share code, notes, and snippets.

@shtukas
Created May 9, 2022 20:29
Show Gist options
  • Save shtukas/5fb988a626558f29f4adad10fa0fc32e to your computer and use it in GitHub Desktop.
Save shtukas/5fb988a626558f29f4adad10fa0fc32e to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby
# encoding: UTF-8
require 'json'
# JSON.pretty_generate(object)
require 'date'
require 'time'
require 'securerandom'
# SecureRandom.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
# SecureRandom.hex(4) #=> "eb693123"
# SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
require 'fileutils'
# FileUtils.mkpath '/a/b/c'
# FileUtils.cp(src, dst)
# FileUtils.mv 'oldname', 'newname'
# FileUtils.rm(path_to_image)
# FileUtils.rm_rf('dir/to/remove')
require 'digest/sha1'
# Digest::SHA1.hexdigest 'foo'
# Digest::SHA1.file(myFile).hexdigest
# Digest::SHA256.hexdigest 'message'
# Digest::SHA256.file(myFile).hexdigest
require 'sqlite3'
require 'zlib' # To compute CRC check
# crc = Zlib::crc32("Hello")
# -----------------------------------------------------------------------
require "/Users/pascal/Galaxy/LucilleOS/Libraries/Ruby-Libraries/Marbles.rb"
require "/Users/pascal/Galaxy/LucilleOS/Libraries/Ruby-Libraries/AionCore.rb"
=begin
The operator is an object that has meet the following signatures
.commitBlob(blob: BinaryData) : Hash
.filepathToContentHash(filepath) : Hash
.readBlobErrorIfNotFound(nhash: Hash) : BinaryData
.datablobCheck(nhash: Hash): Boolean
class Elizabeth
def initialize()
end
def commitBlob(blob)
nhash = "SHA256-#{Digest::SHA256.hexdigest(blob)}"
XCache::set("SHA256-#{Digest::SHA256.hexdigest(blob)}", blob)
nhash
end
def filepathToContentHash(filepath)
"SHA256-#{Digest::SHA256.file(filepath).hexdigest}"
end
def readBlobErrorIfNotFound(nhash)
blob = XCache::getOrNull(nhash)
raise "[Elizabeth error: fc1dd1aa]" if blob.nil?
blob
end
def datablobCheck(nhash)
begin
readBlobErrorIfNotFound(nhash)
true
rescue
false
end
end
end
AionCore::commitLocationReturnHash(operator, location)
AionCore::exportHashAtFolder(operator, nhash, targetReconstructionFolderpath)
AionFsck::structureCheckAionHash(operator, nhash)
=end
# ----------------------------------------------------------------------------
=begin
https://en.wikipedia.org/wiki/Portable_Network_Graphics
PNG-Gradient.png
89504E47 0D0A1A0A 0000000D 49484452 00000080 00000044 08020000 00C625AA 3E000000 D4494441 54785EEC D1810900 3008C030 15FF3FD9 ED0C4152 FA4172F2 4DC5D6AE D84C0700 00080000 01002000 00040080 00001000 00020040 00000800 00010020 00000400 80000010 00000200 40DDBF9D 3A586118 040228B8 98A6FFFF BF6E6C49 212CA9E0 A1BDCE43 8251E365 20A33DA2 C6166DAF D71FC674 C9B4B89D CF9A3F6B F1B6BECF 8B755BED B67C458E 3846E435 FAE7B59F F3EFAD9C 8F5D076A 525FDDCF 2CEE59DD BC1E2DFE 4A7E4100 04008000 00100000 02004000 00080000 01002000 00040080 00001000 00020040 00000800 000100A0 37497F02 9902C055 91000000 0049454E 44AE4260 82
PNG-Gradient.png
prefix: 89504E47 0D0A1A0A
length: 0000000D (13) # PNGStoreRepresentationTransforms::stringHexToInteger("0000000D")
type : 49484452 (IHDR) # PNGStoreRepresentationTransforms::stringHexToAscii("49484452")
data : 00000080 00000044 08020000 00
CRC : C625AA3E
length: 000000D4 (212) # PNGStoreRepresentationTransforms::stringHexToInteger("000000D4")
type : 49444154 (IDAT) # PNGStoreRepresentationTransforms::stringHexToAscii("49444154")
data : 785EECD18109003008C0 (10)
3015FF3FD9ED0C4152FA (20)
4172F24DC5D6AED84C07 (30)
00000800000100200000 (40)
04008000001000000200 (50)
40000008000001002000 (60)
00040080000010000002 (70)
0040DDBF9D3A58611804 (80)
0228B898A6FFFFBF6E6C (90)
49212CA9E0A1BDCE4382 (100)
51E36520A33DA2C6166D (110)
AFD71FC674C9B4B89DCF (120)
9A3F6BF1B6BECF8B755B (130)
EDB67C458E3846E435FA (140)
E7B59FF3EFAD9C8F5D07 (150)
6A525FDDCF2CEE59DDBC (160)
1E2DFE4A7E4100040080 (170)
00001000000200400000 (180)
08000001002000000400 (190)
80000010000002004000 (200)
000800000100A037497F (210)
0299 (212)
crc : 02C05591
length: 00000000
type : 49454E44 (IEND)
data :
crc : AE426082
=end
=begin
https://apidock.com/ruby/String/unpack
# Remember: a Byte is 8bits, 2 hexadeximal values
https://ruby-doc.org/core-2.1.1/Array.html#method-i-pack
https://ruby-doc.org/core-2.5.0/IO.html#method-i-read
f: filehandler instance
f.read([length [, outbuf]]) # Reads length bytes from the I/O stream.
seek(amount, whence=IO::SEEK_SET) → 0
Seeks to a given offset anInteger in the stream according to the value of whence:
:CUR or IO::SEEK_CUR | Seeks to _amount_ plus current position
----------------------+--------------------------------------------------
:END or IO::SEEK_END | Seeks to _amount_ plus end of stream (you
| probably want a negative value for _amount_)
----------------------+--------------------------------------------------
:SET or IO::SEEK_SET | Seeks to the absolute location given by _amount_
=end
# ----------------------------------------------------------------------------
class PNGStoreUtils
# PNGStoreUtils::pascalPrivateChuckTypeName()
def self.pascalPrivateChuckTypeName()
[
"a", # Lowercase indicates that the chunk is not critical (at opposition to "ancillary")
"b", # Lowercase indicates that the chunk is private (at opposition to public)
"C", # Third letter must be uppercase to conform to the PNG specification
"d" # Case indicates whether the chunk is safe to copy by editors that do not recognize it.
# If lowercase, the chunk may be safely copied regardless of the extent of modifications to the file.
# If uppercase, it may only be copied if the modifications have not touched any critical chunks.
].join()
# 27**4 = 531,441 possibilities
end
end
class PNGStoreRepresentationTransforms
# -- data to ------------------------
# PNGStoreRepresentationTransforms::dataToStringHex(data)
def self.dataToStringHex(data)
data.unpack('H*').join()
end
# PNGStoreRepresentationTransforms::dataToStringBinary(data)
def self.dataToStringBinary(data)
data.unpack('b*').join()
end
# PNGStoreRepresentationTransforms::dataToInteger(data)
def self.dataToInteger(data)
PNGStoreRepresentationTransforms::dataToStringHex(data).to_i(16)
end
# -- string hex to ------------------------
# PNGStoreRepresentationTransforms::stringHexToInteger(str)
def self.stringHexToInteger(str)
str.to_i(16)
end
# PNGStoreRepresentationTransforms::stringHexToAscii(str)
def self.stringHexToAscii(str)
[str].pack('H*')
end
# PNGStoreRepresentationTransforms::stringHexToData(str)
def self.stringHexToData(str)
[str].pack('H*')
end
# -- integer to ------------------------
# PNGStoreRepresentationTransforms::integerToStringHex(int)
def self.integerToStringHex(int)
int.to_s(16)
end
# PNGStoreRepresentationTransforms::integerToDataEnsure4Bytes(int)
def self.integerToDataEnsure4Bytes(int)
PNGStoreRepresentationTransforms::stringHexToData(PNGStoreRepresentationTransforms::integerToStringHex(int).rjust(8, "0"))
end
# PNGStoreRepresentationTransforms::typeAndDataToCRCData(type, data)
def self.typeAndDataToCRCData(type, data)
[Zlib::crc32(type+data).to_s(16).rjust(8, "0")].pack('H*')
end
end
class PNGStoreChunkAlgebra
# PNGStoreChunkAlgebra::algebraicUpgradeSx01(sx01, data)
def self.algebraicUpgradeSx01(sx01, data)
sx01 = sx01.clone
prefix = sx01[0]
chunks = sx01.drop(1)
lastchunk = chunks.pop()
# Removing any present abCd chunk
chunks = chunks.select{|chunk| chunk["type"] != PNGStoreUtils::pascalPrivateChuckTypeName() }
ourchunk = {
"length" => PNGStoreRepresentationTransforms::integerToDataEnsure4Bytes( PNGStoreRepresentationTransforms::dataToStringHex(data).size/2 ), # data.size wasn't working, we need the length in bytes
"type" => PNGStoreUtils::pascalPrivateChuckTypeName(),
"data" => data,
"crc" => PNGStoreRepresentationTransforms::typeAndDataToCRCData(PNGStoreUtils::pascalPrivateChuckTypeName(), data)
}
[prefix] + chunks + [ourchunk] + [lastchunk]
end
# PNGStoreChunkAlgebra::extractDataFromSx01(sx01)
def self.extractDataFromSx01(sx01)
sx01.shift
sx01
.map{|chunk|
if chunk["type"] == PNGStoreUtils::pascalPrivateChuckTypeName() then
chunk["data"]
else
nil
end
}
.compact
.join()
end
end
class PNGIO
# PNGIO::pngPrefixAsData()
def self.pngPrefixAsData()
PNGStoreRepresentationTransforms::stringHexToData("89504E470D0A1A0A")
end
# PNGIO::filepathToStructureSx01(filepath)
def self.filepathToStructureSx01(filepath)
# Sx01 = [ prefix, chunk, ..., chunk]
# chunk {
# "length" => length # Data
# "type" => type # Data
# "data" => data # Data
# "crc" => crc # Data
# }
array = []
f = File.open(filepath)
array << f.read(8)
while !f.eof? do
length = f.read(4)
type = f.read(4)
data = f.read(PNGStoreRepresentationTransforms::dataToInteger(length))
crc = f.read(4)
if PNGStoreRepresentationTransforms::typeAndDataToCRCData(type, data) != crc then
puts "(warning) Looks like we have a malformed chunk! (#{type})" # This forces our computation to be correct
end
chunk = {
"length" => length,
"type" => type,
"data" => data,
"crc" => crc,
}
array << chunk
end
f.close
array
end
# PNGIO::sendSx01ToDisk(filepath, sx01)
def self.sendSx01ToDisk(filepath, sx01)
sx01 = sx01.clone
f = File.open(filepath, "w")
f.write(sx01.shift)
sx01.each{|chunk|
f.write(chunk["length"])
f.write(chunk["type"])
f.write(chunk["data"])
f.write(chunk["crc"])
}
f.close
end
# PNGIO::printSx01AsTable(sx01)
def self.printSx01AsTable(sx01)
sx01 = sx01.clone
puts "-------------------------------------------------"
puts " length | type | data | crc32"
puts "-------------------------------------------------"
sx01.shift
sx01
.each{|chunk|
data2 = PNGStoreRepresentationTransforms::dataToStringHex(chunk["data"])
data3 = data2.size > 10 ? "(...)" : " "
data4 = "#{data2[0, 10].rjust(10)} #{data3}"
puts "#{PNGStoreRepresentationTransforms::dataToInteger(chunk["length"]).to_s.rjust(10)} | #{chunk["type"]} | #{data4} | #{PNGStoreRepresentationTransforms::dataToInteger(chunk["crc"])}"
}
puts "-------------------------------------------------"
end
# PNGIO::extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
def self.extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
sx01 = PNGIO::filepathToStructureSx01(picturefilepath)
data = PNGStoreChunkAlgebra::extractDataFromSx01(sx01)
if !data.start_with?("SQLite format 3") then
# We create the database and return the databasefilepath
databasefilename = "#{SecureRandom.hex}.sqlite3"
databasefilepath = "/tmp/#{databasefilename}"
Marbles::issueNewEmptyMarbleFile(databasefilepath)
return databasefilepath
else
databasefilename = "#{SecureRandom.hex}.sqlite3"
databasefilepath = "/tmp/#{databasefilename}"
File.open(databasefilepath, "w"){|f| f.write(data) }
return databasefilepath
end
end
end
# ----------------------------------------------------------------------------
if ARGV[0] == "chunks" and ARGV[1] then
filepath = ARGV[1]
sx01 = PNGIO::filepathToStructureSx01(filepath)
PNGIO::printSx01AsTable(sx01)
exit
end
if ARGV[0] == "set-text" and ARGV[1] and ARGV[2] then
filepath = ARGV[1]
line = ARGV[2]
sx01 = PNGIO::filepathToStructureSx01(filepath)
sx01 = PNGStoreChunkAlgebra::algebraicUpgradeSx01(sx01, line)
PNGIO::sendSx01ToDisk(filepath, sx01)
exit
end
if ARGV[0] == "get-text" and ARGV[1] then
filepath = ARGV[1]
sx01 = PNGIO::filepathToStructureSx01(filepath)
sx01.shift
sx01.each{|chunk|
if chunk["type"] == PNGStoreUtils::pascalPrivateChuckTypeName() then
puts chunk["data"]
end
}
exit
end
if ARGV[0] == "set-data" and ARGV[1] then
filepath = ARGV[1]
data = STDIN.read()
sx01 = PNGIO::filepathToStructureSx01(filepath)
sx01 = PNGStoreChunkAlgebra::algebraicUpgradeSx01(sx01, data)
PNGIO::sendSx01ToDisk(filepath, sx01)
exit
end
if ARGV[0] == "get-data" and ARGV[1] then
filepath = ARGV[1]
sx01 = PNGIO::filepathToStructureSx01(filepath)
print PNGStoreChunkAlgebra::extractDataFromSx01(sx01)
exit
end
if ARGV[0] == "kv-store" and ARGV[1] == "set-text" and ARGV[2] and ARGV[3] and ARGV[4] then
picturefilepath = ARGV[2]
key = ARGV[3]
value = ARGV[4]
databasefilepath = PNGIO::extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
Marbles::set(databasefilepath, key, value)
sx01 = PNGIO::filepathToStructureSx01(picturefilepath)
sx01 = PNGStoreChunkAlgebra::algebraicUpgradeSx01(sx01, IO.read(databasefilepath))
PNGIO::sendSx01ToDisk(picturefilepath, sx01)
exit
end
if ARGV[0] == "kv-store" and ARGV[1] == "get-text" and ARGV[2] and ARGV[3] then
picturefilepath = ARGV[2]
key = ARGV[3]
databasefilepath = PNGIO::extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
puts Marbles::get(databasefilepath, key)
exit
end
if ARGV[0] == "ca-store" and ARGV[1] == "set-text" and ARGV[2] and ARGV[3] then
picturefilepath = ARGV[2]
value = ARGV[3]
key = Digest::SHA256.hexdigest(value)
databasefilepath = PNGIO::extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
Marbles::set(databasefilepath, key, value)
sx01 = PNGIO::filepathToStructureSx01(picturefilepath)
sx01 = PNGStoreChunkAlgebra::algebraicUpgradeSx01(sx01, IO.read(databasefilepath))
PNGIO::sendSx01ToDisk(picturefilepath, sx01)
puts key
exit
end
if ARGV[0] == "ca-store" and ARGV[1] == "get-text" and ARGV[2] and ARGV[3] then
picturefilepath = ARGV[2]
key = ARGV[3]
databasefilepath = PNGIO::extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
puts Marbles::get(databasefilepath, key)
exit
end
if ARGV[0] == "aion" and ARGV[1] == "put-location" and ARGV[2] and ARGV[3] then
picturefilepath = ARGV[2]
location = ARGV[3]
databasefilepath = PNGIO::extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
operator = MarblesElizabeth.new(databasefilepath)
puts AionCore::commitLocationReturnHash(operator, location)
sx01 = PNGIO::filepathToStructureSx01(picturefilepath)
sx01 = PNGStoreChunkAlgebra::algebraicUpgradeSx01(sx01, IO.read(databasefilepath))
PNGIO::sendSx01ToDisk(picturefilepath, sx01)
exit
end
if ARGV[0] == "aion" and ARGV[1] == "get-blob" and ARGV[2] and ARGV[3] then
picturefilepath = ARGV[2]
nhash = ARGV[3]
databasefilepath = PNGIO::extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
puts Marbles::readBlobErrorIfNotFound(databasefilepath, nhash)
exit
end
if ARGV[0] == "aion" and ARGV[1] == "export" and ARGV[2] and ARGV[3] then
picturefilepath = ARGV[2]
nhash = ARGV[3]
databasefilepath = PNGIO::extractSQLIteDataOrMakeANewFileReturnFilepath(picturefilepath)
operator = MarblesElizabeth.new(databasefilepath)
AionCore::exportHashAtFolder(operator, nhash, "/Users/pascal/Desktop")
exit
end
puts "usage:"
puts " png-store chunks <path-to-picture> # show the chunks of the file"
puts " png-store set-text <path-to-picture> <line>"
puts " png-store get-text <path-to-picture> # echo the line to STDOUT"
puts " png-store set-data <path-to-picture> # read from STDIN and insert the data as added content"
puts " png-store get-data <path-to-picture> # echo the added content to STDOUT"
puts " png-store kv-store set-text <path-to-picture> <key> <value-line>"
puts " png-store kv-store get-text <path-to-the-picture> <key> # echo the line to STDOUT"
puts " png-store ca-store set-text <path-to-picture> <value-line> # read the line and insert it in the kv-store content-addressed, return the nhash"
puts " png-store ca-store get-text <path-to-picture> <nhash> # read the line from the kv-store against this nhash and echo to the STDOUT"
puts " png-store aion put-location <path-to-picture> <location> # inject the location as aion structure in the kv-store"
puts " png-store aion get-blob <path-to-picture> <nhash> # echo the blob at the STDOUT"
puts " png-store aion export <path-to-picture> <nhash> # export the aion hierarchy on the desktop"
puts " png-store aion history # shows the history and interactively select a past snapshot to export"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment