Skip to content

Instantly share code, notes, and snippets.

@hyrious
Created August 31, 2023 09:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyrious/6caa045111772177d7c568b9dc64a595 to your computer and use it in GitHub Desktop.
Save hyrious/6caa045111772177d7c568b9dc64a595 to your computer and use it in GitHub Desktop.
backup
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="style.css">
<script src="favicon.js"></script>
<script src="theme.js"></script>
<title>Ruby Marshal Visualization</title>
<style>
.title a { text-decoration: none }
main, section, article { margin: 0 5%; padding: 10px 0 }
p { margin: 10px 5% }
body.active { box-shadow: inset 0 0 0 5px }
.array::before { content: "["; color: grey }
.array { border: 1px solid var(--control); padding: 4px; display: inline-flex; flex-flow: row wrap; gap: 3px }
.fixnum { color: var(--link); }
.bignum { color: cyan }
.string { color: green }
.string:before, .string:after { content: '"' }
.symbol { color: pink }
.symbol:before { content: ":" }
.hash:before { content: "{" }
.hash:after { content: " }" }
.ivars:before { content: "#{" }
.ivars:after { content: "}" }
.symbol_ref { color: pink; border: 1px dashed; }
.object_ref { color: orange; border: 1px dashed; }
.content { border: 1px solid red }
.objects>* { border-bottom: 1px solid orange }
.symbols>* { border-bottom: 1px solid pink; }
.objects, .symbols { display: flex; flex-flow: column nowrap; }
</style>
</head>
<body>
<h1 class="title">Ruby Marshal Visualization<sup><a href="https://github.com/ruby/ruby/blob/master/doc/marshal.rdoc" target="_blank">[?]</a></sup></h1>
<p>Drop or <a id="upload" href="javascript: void 0">Upload</a> a Ruby marshal file here, e.g. Scripts.rvdata2</p>
<main id="output"></main>
<script src="dnu.js"></script>
<script>
dropAndUpload(document.body, upload, async function(file) {
output.textContent = ''
var buffer = await file.arrayBuffer()
var loader = new Loader(new DataView(buffer))
while (loader.hasNext()) {
var content = document.createElement('div')
content.className = 'content'
output.append(content)
var obj = loader.get()
print(content, obj)
var objects = document.createElement('div')
objects.className = 'objects'
output.append(objects)
loader.objects.forEach(o => {
print(objects, o)
})
var symbols = document.createElement('div')
symbols.className = 'symbols'
output.append(symbols)
loader.symbols.forEach(s => {
print(symbols, s)
})
}
})
function h(cls, ...args) {
var e = document.createElement('span')
e.className = cls
e.append(...args)
return e
}
var decoder = new TextDecoder('utf8', { fatal: true })
var indexOf = Array.prototype.indexOf
function print(root, obj) {
if (obj.string) {
root.append(h('string', decode(obj.string)))
} else if (obj.symbol) {
root.append(h('symbol', decode(obj.symbol)))
} else if (obj.array) {
var el = h('array')
root.append(el)
obj.array.forEach(e => { print(el, e); el.append(', ') })
} else if (obj.extends) {
var a = obj.extends // {obj, extends: []}
var el = h('extends')
root.append(el)
a.extends.forEach(e => { print(el, e); el.append(' e> ') })
print(el, a.obj)
} else if (obj.object) {
var a = obj.object
var el = h('object')
root.append(el)
el.append('#<')
print(el, a.name)
a.ivars.forEach(([k, v]) => {
el.append(' ')
print(el, k)
el.append('=')
print(el, v)
})
el.append('>')
} else if (obj.hash) {
var a = obj.hash
var el = h('hash')
root.append(el)
a.entries.forEach(([k, v]) => {
el.append(' ')
print(el, k)
el.append('=>')
print(el, v)
})
if (a.default) {
el.append(' @default=')
print(el, a.default)
}
} else if (obj.ivars) {
var a = obj.ivars
var el = h('ivars')
root.append(el)
a.ivars.forEach(([k, v]) => {
print(el, k)
el.append('=')
print(el, v)
el.append(' ')
})
print(el, a.obj)
} else {
// console.log(obj)
var k = Object.keys(obj)[0]
root.append(h(k, JSON.stringify(obj[k])))
}
}
function decode(data) {
if (indexOf.call(data.subarray(0, 8000), 0) >= 0) return binary(data)
try {
return decoder.decode(data)
} catch {
return binary(data)
}
}
function binary(data) {
return `<${data.byteLength} bytes>`
}
class Loader {
constructor(view) {
this.pos = 0
this.view = view
this.objects = null
this.symbols = null
}
hasNext() {
return this.pos + 2 < this.view.byteLength && this.view.getInt16(this.pos) === 0x408
}
get() {
this.pos += 2
this.objects = []
this.symbols = []
return this.#read_any()
}
get #buffer() {
return this.view.buffer
}
get #byteOffset() {
return this.view.byteOffset
}
#read_bytes(n) {
var ret = new Uint8Array(this.#buffer, this.#byteOffset + this.pos, n)
this.pos += n
return ret
}
#read_fixnum() {
var t = this.view.getInt8(this.pos++)
if (t == 0) return 0
if (-4 <= t && t <= 4) {
var n = Math.abs(t),
shift = (4 - n) * 8,
bytes = this.#read_bytes(n),
a = 0
for (var i = n - 1; i >= 0; --i)
a = a << 8 | bytes[i]
return t > 0 ? a : a << shift >> shift
} else
return t > 0 ? t - 5 : t + 5
}
#read_chunk() {
return this.#read_bytes(this.#read_fixnum())
}
#read_string() {
return { string: this.#read_chunk() }
}
#read_symbol() {
return { symbol: this.#read_chunk() }
}
#remember_object(o) {
return this.objects.push(o), o
}
#remember_symbol(s) {
return this.symbols.push(s), s
}
#read_entries(f) {
var entries = [], n = this.#read_fixnum()
while (n--)
if (f) f(this.#read_any(), this.#read_any())
else entries.push([this.#read_any(), this.#read_any()])
return f ? void 0 : entries
}
#read_bignum() {
var sign = this.view.getUint8(this.pos++),
n = this.#read_fixnum() * 2,
bytes = this.#read_bytes(n),
a = 0
for (var i = 0; i < n; ++i)
a += bytes[i] * 2 ** (i * 8)
return { bignum: sign === 43 ? a : -a }
}
#read_float() {
var bytes = this.#read_chunk(),
s = new TextDecoder().decode(bytes)
return { float: s == 'inf' ? 1/0 : s == '-inf' ? -1/0 : s == 'nan' ? NaN : Number(s) }
}
#read_regexp() {
var bytes = this.#read_chunk(),
type = this.view.getUint8(this.pos++)
return { regexp: { source: bytes, type } }
}
#read_array(n, to) {
for (var i = 0; i < n; ++i)
to[i] = this.#read_any()
return to
}
#read_any() {
var c = this.view.getUint8(this.pos++)
// console.log(String.fromCharCode(c), c)
switch (c) {
case 84: return { true: true }
case 70: return { false: true }
case 48: return { nil: true }
case 105: return { fixnum: this.#read_fixnum() }
case 58: return this.#remember_symbol(this.#read_symbol())
case 59: return { symbol_ref: this.#read_fixnum() }
case 64: return { object_ref: this.#read_fixnum() }
case 73: {
var obj = this.#remember_object(this.#read_any())
var ivars = this.#read_entries()
return { ivars: { obj, ivars } }
}
case 101: {
var extends_ = [this.#read_any()]
while (this.view.getUint8(this.pos) === 101)
this.pos++, extends_.push(this.#read_any())
var obj = this.#remember_object(this.#read_any())
return { extends: { obj, extends: extends_ } }
}
case 91: {
var n = this.#read_fixnum(),
obj = this.#remember_object({ array: new Array(n) })
this.#read_array(n, obj.array)
return obj
}
case 108: return this.#remember_object(this.#read_bignum())
case 99: return this.#remember_object({ class: this.#read_chunk() })
case 109: return this.#remember_object({ module: this.#read_chunk() })
case 77: return this.#remember_object({ class_or_module: this.#read_chunk() })
case 100: {
var name = this.#read_any()
var data = this.#read_any()
return this.#remember_object({ data: { name, data } })
}
case 102: return this.#remember_object(this.#read_float())
case 123: {
var obj = this.#remember_object({ hash: {} })
obj.hash.entries = this.#read_entries()
return obj
}
case 125: {
var obj = this.#remember_object({ hash: {} })
obj.hash.entries = this.#read_entries()
obj.hash.default = this.#read_any()
return obj
}
case 111: {
var obj = this.#remember_object({ object: { name: this.#read_any() } })
obj.object.ivars = this.#read_entries()
return obj
}
case 47: return this.#remember_object(this.#read_regexp())
case 34: return this.#remember_object(this.#read_string())
case 83: {
var obj = this.#remember_object({ struct: { name: this.#read_any() } })
obj.struct.members = this.#read_entries()
return obj
}
case 67: {
var obj = this.#remember_object({ wrapped: { name: this.#read_any() } })
obj.wrapped.wrapped = this.#read_any()
return obj
}
case 117: {
var obj = this.#remember_object({ userDefined: { name: this.#read_any() } })
obj.userDefined.userDefined = this.#read_chunk()
return obj
}
case 85: {
var obj = this.#remember_object({ userMarshal: { name: this.#read_any() } })
obj.userMarshal.userMarshal = this.#read_any()
return obj
}
default: {
return { unknown: this.view.getUint8(this.pos - 1) }
}
}
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment