diff --git a/README.md b/README.md index a2fa0e5e..1d0f88c6 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ In summary it aims to be jq, hexdump, dd and gdb for files combined into one. [aac_frame](doc/formats.md#aac_frame), adts, adts_frame, +aiff, amf0, apev2, [apple_bookmark](doc/formats.md#apple_bookmark), diff --git a/doc/formats.md b/doc/formats.md index 4ce07db4..e7e51a67 100644 --- a/doc/formats.md +++ b/doc/formats.md @@ -7,6 +7,7 @@ |[`aac_frame`](#aac_frame) |Advanced Audio Coding frame || |`adts` |Audio Data Transport Stream |`adts_frame`| |`adts_frame` |Audio Data Transport Stream frame |`aac_frame`| +|`aiff` |Audio Interchange File Format || |`amf0` |Action Message Format 0 || |`apev2` |APEv2 metadata tag |`image`| |[`apple_bookmark`](#apple_bookmark) |Apple BookmarkData || @@ -123,7 +124,7 @@ |`ip_packet` |Group |`icmp` `icmpv6` `tcp_segment` `udp_datagram`| |`link_frame` |Group |`bsd_loopback_frame` `ether8023_frame` `ipv4_packet` `ipv6_packet` `sll2_packet` `sll_packet`| |`mp3_frame_tags` |Group |`mp3_frame_vbri` `mp3_frame_xing`| -|`probe` |Group |`adts` `apple_bookmark` `ar` `avi` `avro_ocf` `bitcoin_blkdat` `bplist` `bzip2` `elf` `flac` `gif` `gzip` `jpeg` `json` `jsonl` `macho` `macho_fat` `matroska` `mp3` `mp4` `mpeg_ts` `ogg` `pcap` `pcapng` `png` `tar` `tiff` `toml` `tzif` `wasm` `wav` `webp` `xml` `yaml` `zip`| +|`probe` |Group |`adts` `aiff` `apple_bookmark` `ar` `avi` `avro_ocf` `bitcoin_blkdat` `bplist` `bzip2` `elf` `flac` `gif` `gzip` `jpeg` `json` `jsonl` `macho` `macho_fat` `matroska` `mp3` `mp4` `mpeg_ts` `ogg` `pcap` `pcapng` `png` `tar` `tiff` `toml` `tzif` `wasm` `wav` `webp` `xml` `yaml` `zip`| |`tcp_stream` |Group |`dns_tcp` `rtmp` `tls`| |`udp_payload` |Group |`dns`| diff --git a/doc/formats.svg b/doc/formats.svg index ebb629bb..63bc3eec 100644 --- a/doc/formats.svg +++ b/doc/formats.svg @@ -4,11 +4,11 @@ - + formats - + adts @@ -46,10 +46,10 @@ apev2 - -apev2 - -image + +apev2 + +image @@ -60,8 +60,8 @@ apev2:e->image:n - - + + @@ -74,7 +74,7 @@ icc_profile - + image->jpeg:n @@ -134,7 +134,7 @@ vpx_ccr - + image->mp4:n @@ -150,7 +150,7 @@ exif - + image->png:n @@ -164,7 +164,7 @@ icc_profile - + image->tiff:n @@ -178,22 +178,22 @@ vp8_frame - + image->webp:n - + gif - -gif + +gif - + image->gif:n - - + + @@ -206,25 +206,25 @@ probe - -probe + +probe ar:e->probe:n - - + + probe->adts:n - - + + - + probe->ar:n - + @@ -242,9 +242,9 @@ flac_frame - + probe->avi:n - + @@ -256,10 +256,10 @@ bitcoin_block - + probe->bitcoin_blkdat:n - - + + @@ -270,9 +270,9 @@ probe - + probe->bzip2:n - + @@ -286,9 +286,9 @@ flac_frame - + probe->flac:n - + @@ -300,15 +300,15 @@ probe - + probe->gzip:n - - + + - + probe->jpeg:n - + @@ -320,10 +320,10 @@ macho - + probe->macho_fat:n - - + + @@ -332,9 +332,9 @@ macho - + probe->macho:n - + @@ -382,43 +382,43 @@ vp9_frame - + probe->matroska:n - + mp3 - -mp3 - -id3v2 - -id3v1 - -id3v11 - -apev2 - -mp3_frame + +mp3 + +id3v2 + +id3v1 + +id3v11 + +apev2 + +mp3_frame - + probe->mp3:n - - + + - + probe->mp4:n - - + + - + probe->png:n - + @@ -438,254 +438,266 @@ flac_frame - + probe->ogg:n - + pcap - -pcap - -link_frame - -tcp_stream - -ipv4_packet + +pcap + +link_frame + +tcp_stream + +ipv4_packet - + probe->pcap:n - - + + pcapng - -pcapng - -link_frame - -tcp_stream - -ipv4_packet + +pcapng + +link_frame + +tcp_stream + +ipv4_packet - + probe->pcapng:n - - + + tar - -tar - -probe + +tar + +probe - + probe->tar:n - - + + - + probe->tiff:n - - + + wav - -wav - -id3v2 - -id3v1 - -id3v11 + +wav + +id3v2 + +id3v1 + +id3v11 - + probe->wav:n - - + + - + probe->webp:n - + zip - -zip - -probe + +zip + +probe - + probe->zip:n - - + + + + + +aiff + +aiff + + + +probe->aiff:n + + - + apple_bookmark - -apple_bookmark + +apple_bookmark - + probe->apple_bookmark:n - - + + - + avro_ocf - -avro_ocf + +avro_ocf - + probe->avro_ocf:n - - + + - + bplist - -bplist + +bplist - + probe->bplist:n - - + + - + elf - -elf + +elf - + probe->elf:n - - + + - + probe->gif:n - - + + - + json - -json + +json - + probe->json:n - - + + - + jsonl - -jsonl + +jsonl - + probe->jsonl:n - - + + - + mpeg_ts - -mpeg_ts + +mpeg_ts - + probe->mpeg_ts:n - - + + - + toml - -toml + +toml - + probe->toml:n - - + + - + tzif - -tzif + +tzif - + probe->tzif:n - - + + - + wasm - -wasm + +wasm - + probe->wasm:n - - + + - + xml - -xml + +xml - + probe->xml:n - - + + - + yaml - -yaml + +yaml - + probe->yaml:n - - + + @@ -916,70 +928,70 @@ bsd_loopback_frame - -bsd_loopback_frame - -inet_packet + +bsd_loopback_frame + +inet_packet inet_packet - -inet_packet + +inet_packet bsd_loopback_frame:e->inet_packet:n - - + + ipv4_packet - -ipv4_packet - -ip_packet + +ipv4_packet + +ip_packet - + inet_packet->ipv4_packet:n - - + + ipv6_packet - -ipv6_packet - -ip_packet + +ipv6_packet + +ip_packet - + inet_packet->ipv6_packet:n - - + + bzip2:e->probe:n - - + + ether8023_frame - -ether8023_frame - -inet_packet + +ether8023_frame + +inet_packet ether8023_frame:e->inet_packet:n - - + + @@ -1062,8 +1074,8 @@ flac_picture:e->image:n - - + + @@ -1074,8 +1086,8 @@ gzip:e->probe:n - - + + @@ -1158,94 +1170,94 @@ ip_packet - -ip_packet + +ip_packet ipv4_packet:e->ip_packet:n - - + + udp_datagram - -udp_datagram - -udp_payload + +udp_datagram + +udp_payload - + ip_packet->udp_datagram:n - - + + - + icmp - -icmp + +icmp - + ip_packet->icmp:n - - + + - + icmpv6 - -icmpv6 + +icmpv6 - + ip_packet->icmpv6:n - - + + - + tcp_segment - -tcp_segment + +tcp_segment - + ip_packet->tcp_segment:n - - + + ipv6_packet:e->ip_packet:n - - + + exif - -exif + +exif jpeg:e->exif:n - - + + icc_profile - -icc_profile + +icc_profile jpeg:e->icc_profile:n - - + + @@ -1262,8 +1274,8 @@ matroska:e->image:n - - + + @@ -1440,65 +1452,65 @@ mp3:e->apev2:n - - + + mp3:e->mp3_frame:n - + mp3:e->id3v2:n - - + + id3v1 - -id3v1 + +id3v1 mp3:e->id3v1:n - - + + id3v11 - -id3v11 + +id3v11 mp3:e->id3v11:n - - + + - + mp3_frame_vbri mp3_frame_vbri - + mp3_frame_tags->mp3_frame_vbri:n - + mp3_frame_xing mp3_frame_xing - + mp3_frame_tags->mp3_frame_xing:n @@ -1578,8 +1590,8 @@ mp4:e->icc_profile:n - - + + @@ -1698,14 +1710,14 @@ png:e->exif:n - - + + png:e->icc_profile:n - - + + @@ -1722,23 +1734,23 @@ mpeg_pes - -mpeg_pes - -mpeg_pes_packet - -mpeg_spu + +mpeg_pes + +mpeg_pes_packet + +mpeg_spu mpeg_pes:e->mpeg_pes_packet:n - + mpeg_pes:e->mpeg_spu:n - + @@ -1780,240 +1792,240 @@ pcap:e->ipv4_packet:n - - + + link_frame - -link_frame + +link_frame pcap:e->link_frame:n - - + + tcp_stream - -tcp_stream + +tcp_stream pcap:e->tcp_stream:n - - + + - + link_frame->bsd_loopback_frame:n - - + + - + link_frame->ether8023_frame:n - - + + - + link_frame->ipv4_packet:n - - + + - + link_frame->ipv6_packet:n - - + + sll2_packet - -sll2_packet - -inet_packet + +sll2_packet + +inet_packet - + link_frame->sll2_packet:n - - + + sll_packet - -sll_packet - -inet_packet + +sll_packet + +inet_packet - + link_frame->sll_packet:n - - + + rtmp - -rtmp - -amf0 - -mpeg_asc + +rtmp + +amf0 + +mpeg_asc - + tcp_stream->rtmp:n - - + + tls - -tls - -asn1_ber + +tls + +asn1_ber - + tcp_stream->tls:n - - + + - + dns_tcp - -dns_tcp + +dns_tcp - + tcp_stream->dns_tcp:n - - + + pcapng:e->ipv4_packet:n - - + + pcapng:e->link_frame:n - - + + pcapng:e->tcp_stream:n - - + + rtmp:e->mpeg_asc:n - - + + amf0 - -amf0 + +amf0 rtmp:e->amf0:n - - + + sll2_packet:e->inet_packet:n - - + + sll_packet:e->inet_packet:n - - + + tar:e->probe:n - - + + tiff:e->icc_profile:n - - + + asn1_ber - -asn1_ber + +asn1_ber tls:e->asn1_ber:n - - + + udp_payload - -udp_payload + +udp_payload udp_datagram:e->udp_payload:n - - + + - + dns - -dns + +dns - + udp_payload->dns:n - - + + wav:e->id3v2:n - - + + wav:e->id3v1:n - - + + wav:e->id3v11:n - - + + @@ -2024,68 +2036,68 @@ zip:e->probe:n - - + + - + bencode - -bencode + +bencode - + bits - -bits + +bits - + bson - -bson + +bson - + bytes - -bytes + +bytes - + cbor - -cbor + +cbor - + csv - -csv + +csv - + fairplay_spc - -fairplay_spc + +fairplay_spc - + html - -html + +html - + markdown - -markdown + +markdown - + msgpack - -msgpack + +msgpack diff --git a/format/all/all.fqtest b/format/all/all.fqtest index 196f0c2e..068d8f7e 100644 --- a/format/all/all.fqtest +++ b/format/all/all.fqtest @@ -27,6 +27,7 @@ $ fq -n _registry.groups.probe "wasm", "webp", "zip", + "aiff", "mp3", "mpeg_ts", "wav", @@ -40,6 +41,7 @@ $ fq --help formats aac_frame Advanced Audio Coding frame adts Audio Data Transport Stream adts_frame Audio Data Transport Stream frame +aiff Audio Interchange File Format amf0 Action Message Format 0 apev2 APEv2 metadata tag apple_bookmark Apple BookmarkData diff --git a/format/format.go b/format/format.go index 9d9e61b9..19cfd6f2 100644 --- a/format/format.go +++ b/format/format.go @@ -29,6 +29,7 @@ const ( AAC_FRAME = "aac_frame" ADTS = "adts" ADTS_FRAME = "adts_frame" + AIFF = "aiff" AMF0 = "amf0" APEV2 = "apev2" APPLE_BOOKMARK = "apple_bookmark" diff --git a/format/riff/aiff.go b/format/riff/aiff.go new file mode 100644 index 00000000..e4430669 --- /dev/null +++ b/format/riff/aiff.go @@ -0,0 +1,117 @@ +package riff + +// http://midi.teragonaudio.com/tech/aiff.htm + +import ( + "github.com/wader/fq/format" + "github.com/wader/fq/internal/mathex" + "github.com/wader/fq/pkg/decode" + "github.com/wader/fq/pkg/interp" + "github.com/wader/fq/pkg/scalar" +) + +func init() { + interp.RegisterFormat(decode.Format{ + Name: format.AIFF, + ProbeOrder: format.ProbeOrderBinFuzzy, + Description: "Audio Interchange File Format", + Groups: []string{format.PROBE}, + DecodeFn: aiffDecode, + }) +} + +const aiffRiffType = "AIFF" + +// pstring: +// > Pascal-style string, a one-byte count followed by that many text bytes. The total number of bytes in this data type should be even. +// > A pad byte can be added to the end of the text to accomplish this. This pad byte is not reflected in the count. +func aiffPString(d *decode.D) string { + l := d.U8() + pad := (l + 1) % 2 + s := d.UTF8(int(l + pad)) + return s[0 : l+1-pad] +} + +func aiffDecode(d *decode.D) any { + var riffType string + riffDecode( + d, + nil, + func(d *decode.D, path path) (string, int64) { + id := d.FieldUTF8("id", 4, chunkIDDescriptions) + + const restOfFileLen = 0xffffffff + size := int64(d.FieldScalarUintFn("size", func(d *decode.D) scalar.Uint { + l := d.U32() + if l == restOfFileLen { + return scalar.Uint{Actual: l, DisplayFormat: scalar.NumberHex, Description: "Rest of file"} + } + return scalar.Uint{Actual: l, DisplayFormat: scalar.NumberDecimal} + }).Actual) + + if size == restOfFileLen { + size = d.BitsLeft() / 8 + } + return id, size + }, + func(d *decode.D, id string, path path) (bool, any) { + switch id { + case "FORM": + riffType = d.FieldUTF8("format", 4, d.StrAssert(aiffRiffType)) + return true, nil + case "COMT": + numComments := d.FieldU16("num_comments") + d.FieldArray("comments", func(d *decode.D) { + for i := 0; i < int(numComments); i++ { + d.FieldStruct("comment", func(d *decode.D) { + d.FieldU32("timestamp") + d.FieldU16("marker_id") + count := d.FieldU16("count") + pad := count % 2 + d.FieldUTF8("text", int(count)) + if pad != 0 { + d.FieldRawLen("pad", int64(pad)*8) + } + }) + } + }) + return false, nil + case "COMM": + d.FieldU16("num_channels") + d.FieldU32("num_sample_frames") + d.FieldU16("sample_size") + // TODO: support big float? + d.FieldFltFn("sample_rate", func(d *decode.D) float64 { + return mathex.NewFloat80FromBytes(d.BytesLen(10)).Float64() + }) + return false, nil + case "SSND": + d.FieldU32("offset") + d.FieldU32("block_size") + d.FieldRawLen("data", d.BitsLeft()) + return false, nil + case "MARK": + numMarkers := d.FieldU16("num_markers") + d.FieldArray("markers", func(d *decode.D) { + for i := 0; i < int(numMarkers); i++ { + d.FieldStruct("marker", func(d *decode.D) { + d.FieldU16("id") + d.FieldU32("position") + d.FieldStrFn("name", aiffPString) + }) + } + }) + return false, nil + default: + d.FieldRawLen("data", d.BitsLeft()) + return false, nil + } + }, + ) + + if riffType != aiffRiffType { + d.Errorf("wrong or no AIFF riff type found (%s)", riffType) + } + + return nil +} diff --git a/format/riff/testdata/sox.aiff b/format/riff/testdata/sox.aiff new file mode 100644 index 00000000..8179aa4f Binary files /dev/null and b/format/riff/testdata/sox.aiff differ diff --git a/format/riff/testdata/sox.aiff.fqtest b/format/riff/testdata/sox.aiff.fqtest new file mode 100644 index 00000000..a90373f7 --- /dev/null +++ b/format/riff/testdata/sox.aiff.fqtest @@ -0,0 +1,35 @@ +# ffmpeg -f lavfi -i sine -t 100ms -f wav sox.wav; sox sox.wav six.aiff +$ fq dv sox.aiff + |00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.{}: sox.aiff (aiff) 0x0-0x22cb.7 (8908) +0x0000|46 4f 52 4d |FORM | id: "FORM" 0x0-0x3.7 (4) +0x0000| 00 00 22 c4 | ..". | size: 8900 0x4-0x7.7 (4) +0x0000| 41 49 46 46 | AIFF | format: "AIFF" (valid) 0x8-0xb.7 (4) + | | | chunks[0:3]: 0xc-0x22cb.7 (8896) + | | | [0]{}: chunk 0xc-0x2d.7 (34) +0x0000| 43 4f 4d 54| COMT| id: "COMT" 0xc-0xf.7 (4) +0x0010|00 00 00 1a |.... | size: 26 0x10-0x13.7 (4) +0x0010| 00 01 | .. | num_comments: 1 0x14-0x15.7 (2) + | | | comments[0:1]: 0x16-0x2d.7 (24) + | | | [0]{}: comment 0x16-0x2d.7 (24) +0x0010| e0 2f 99 4b | ./.K | timestamp: 3761215819 0x16-0x19.7 (4) +0x0010| 00 00 | .. | marker_id: 0 0x1a-0x1b.7 (2) +0x0010| 00 10 | .. | count: 16 0x1c-0x1d.7 (2) +0x0010| 50 72| Pr| text: "Processed by SoX" 0x1e-0x2d.7 (16) +0x0020|6f 63 65 73 73 65 64 20 62 79 20 53 6f 58 |ocessed by SoX | + | | | [1]{}: chunk 0x2e-0x47.7 (26) +0x0020| 43 4f| CO| id: "COMM" 0x2e-0x31.7 (4) +0x0030|4d 4d |MM | +0x0030| 00 00 00 12 | .... | size: 18 0x32-0x35.7 (4) +0x0030| 00 01 | .. | num_channels: 1 0x36-0x37.7 (2) +0x0030| 00 00 11 3a | ...: | num_sample_frames: 4410 0x38-0x3b.7 (4) +0x0030| 00 10 | .. | sample_size: 16 0x3c-0x3d.7 (2) +0x0030| 40 0e| @.| sample_rate: 44100 0x3e-0x47.7 (10) +0x0040|ac 44 00 00 00 00 00 00 |.D...... | + | | | [2]{}: chunk 0x48-0x22cb.7 (8836) +0x0040| 53 53 4e 44 | SSND | id: "SSND" 0x48-0x4b.7 (4) +0x0040| 00 00 22 7c| .."|| size: 8828 0x4c-0x4f.7 (4) +0x0050|00 00 00 00 |.... | offset: 0 0x50-0x53.7 (4) +0x0050| 00 00 00 00 | .... | block_size: 0 0x54-0x57.7 (4) +0x0050| 00 00 01 00 01 ff 02 fd| ........| data: raw bits 0x58-0x22cb.7 (8820) +0x0060|03 f8 04 ee 05 e0 06 cc 07 b0 08 8d 09 62 0a 2d|.............b.-| +* |until 0x22cb.7 (end) (8820) | | diff --git a/internal/mathex/float80.go b/internal/mathex/float80.go new file mode 100644 index 00000000..facd118c --- /dev/null +++ b/internal/mathex/float80.go @@ -0,0 +1,215 @@ +// Float80 type from https://github.com/mewspring/mewmew-l +// modified as bit to read bytes instead of hex string +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +package mathex + +import ( + "fmt" + "log" + "math" + "math/big" +) + +// Float80 represents an 80-bit IEEE 754 extended precision floating-point +// value, in x86 extended precision format. +// +// References: +// +// https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format +type Float80 struct { + // Sign and exponent. + // + // 1 bit: sign + // 15 bits: exponent + se uint16 + // Integer part and fraction. + // + // 1 bit: integer part + // 63 bits: fraction + m uint64 +} + +// Bits returns the IEEE 754 binary representation of f, with the sign and +// exponent in se and the mantissa in m. +func (f Float80) Bits() (se uint16, m uint64) { + return f.se, f.m +} + +// Bytes returns the x86 extended precision binary representation of f as a byte +// slice. +func (f Float80) Bytes() []byte { + return []byte(f.String()) +} + +// String returns the IEEE 754 binary representation of f as a string, +// containing 10 bytes in hexadecimal format. +func (f Float80) String() string { + return fmt.Sprintf("%04X%016X", f.se, f.m) +} + +// Float64 returns the float64 representation of f. +func (f Float80) Float64() float64 { + se := uint64(f.se) + m := f.m + // 1 bit: sign + sign := se >> 15 + // 15 bits: exponent + exp := se & 0x7FFF + // Adjust for exponent bias. + // + // === [ binary64 ] ========================================================= + // + // Exponent bias 1023. + // + // +===========================+=======================+ + // | Exponent (in binary) | Notes | + // +===========================+=======================+ + // | 00000000000 | zero/subnormal number | + // +---------------------------+-----------------------+ + // | 00000000001 - 11111111110 | normalized value | + // +---------------------------+-----------------------+ + // | 11111111111 | infinity/NaN | + // +---------------------------+-----------------------+ + // + // References: + // https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Exponent_encoding + exp64 := int64(exp) - 16383 + 1023 + switch { + case exp == 0: + // exponent is all zeroes. + exp64 = 0 + case exp == 0x7FFF: + // exponent is all ones. + exp64 = 0x7FF + default: + } + // 63 bits: fraction + frac := m & 0x7FFFFFFFFFFFFFFF + // Sign, exponent and fraction of binary64. + // + // 1 bit: sign + // 11 bits: exponent + // 52 bits: fraction + // + // References: + // https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64 + bits := sign<<63 | uint64(exp64)<<52 | frac>>11 + return math.Float64frombits(bits) +} + +// BigFloat returns the *big.Float representation of f. +func (f Float80) BigFloat() *big.Float { + x := &big.Float{} + sign := (f.se & 0x8000) != 0 + e := f.se & 0x7FFF + s := fmt.Sprintf("0x.%Xp%d", f.m, e-16383+1) + if sign { + s = "-" + s + } + x.SetPrec(52) + _, _, err := x.Parse(s, 0) + if err != nil { + log.Printf("big.Float.Parse: error %v", err) + } + return x +} + +// NewFloat80FromFloat64 returns the nearest 80-bit floating-point value for x. +func NewFloat80FromFloat64(x float64) Float80 { + // Sign, exponent and fraction of binary64. + // + // 1 bit: sign + // 11 bits: exponent + // 52 bits: fraction + bits := math.Float64bits(x) + // 1 bit: sign + sign := uint16(bits >> 63) + // 11 bits: exponent + exp := bits >> 52 & 0x7FF + // 52 bits: fraction + frac := bits & 0xFFFFFFFFFFFFF + + if exp == 0 && frac == 0 { + // zero value. + return Float80{} + } + + // Sign, exponent and fraction of binary80. + // + // 1 bit: sign + // 15 bits: exponent + // 1 bit: integer part + // 63 bits: fraction + + // 15 bits: exponent. + // + // Exponent bias 1023 (binary64) + // Exponent bias 16383 (binary80) + exp80 := int64(exp) - 1023 + 16383 + // 63 bits: fraction. + // + frac80 := frac << 11 + switch { + case exp == 0: + exp80 = 0 + case exp == 0x7FF: + exp80 = 0x7FFF + } + se := sign<<15 | uint16(exp80) + // Integer part set to specify normalized value. + m := 0x8000000000000000 | frac80 + return NewFloat80FromBits(se, m) +} + +// NewFloat80FromBytes returns a new 80-bit floating-point value based on b, +func NewFloat80FromBytes(b []byte) Float80 { + var f Float80 + if len(b) != 10 { + panic(fmt.Errorf("invalid length of float80 representation, expected 10, got %d", len(b))) + } + f.se = uint16(int64(b[0])<<8 | int64(b[1]<<0)) + f.m = uint64(0 | + int64(b[2])<<56 | + int64(b[3])<<48 | + int64(b[4])<<40 | + int64(b[5])<<32 | + int64(b[6])<<24 | + int64(b[7])<<16 | + int64(b[8])<<8 | + int64(b[9])<<0, + ) + return f +} + +// NewFloat80FromBits returns a new 80-bit floating-point value based on the +// sign, exponent and mantissa bits. +func NewFloat80FromBits(se uint16, m uint64) Float80 { + return Float80{ + se: se, + m: m, + } +}