Little Endian Number to String Conversion

13

Einführung

Während der Arbeit mit dem BMP- Generator (Bitmap- Generator) habe ich das Problem, Zahlen in Little-Endian-Hex-Strings umzuwandeln. Hier ist eine Funktion, die ich in JavaScript erstelle - aber ich frage mich, wie kleiner Code ähnlich funktionieren kann

let liEnd= num => num.toString(16).padStart(8,'0').match(/../g).reverse().join``;
console.log(liEnd(304767)) // 304767 dec = 0x4a67f hex

Herausforderung

Schreibfunktion, die bei der Eingabe eine 32-Bit-Ganzzahl ohne Vorzeichen verwendet und eine 8-stellige hexadezimale Zeichenfolge mit kleiner Endian-Reihenfolge erzeugt. Der Beispielalgorithmus, der die Arbeit erledigt:

  • konvertiere numb in hex string zB: 304767 -> '4a67f'
  • Fügen Sie Füllungsnullen hinzu, um eine Zeichenfolge mit 8 Zeichen zu erhalten: '0004a67f'
  • Saite in vier 2-Zeichen-Stücke teilen: '00','04','a6','7f'
  • umgekehrte Reihenfolge der Teile '7f','a6','04','00'
  • Teile verbinden und als Ergebnis zurückkehren: '7fa60400'

Beispiel für Ein- und Ausgabe

Die Eingangsnummer (oder Zeichenfolge mit Dez.-Nummer) befindet sich links von der Eingabe- ->Hex-Zeichenfolge rechts

2141586432 -> 0004a67f
304767     -> 7fa60400
Kamil Kiełczewski
quelle

Antworten:

7

05AB1E , 10 9 Bytes

žJ+h¦2ôRJ

Probieren Sie es online aus!

-1 Byte durch Inspiration der Jelly-Antwort.

žJ+   add 2^32 to input
h     convert to hex
¦     drop leading 1
2ô    split in groups of 2
R     reverse groups
J     and join them
Dorian
quelle
6

Python 3 , 37 Bytes

lambda n:n.to_bytes(4,"little").hex()

Probieren Sie es online aus!

Arithmetikbasierte rekursive Lösung ( 50 49 Byte, funktioniert auch für Python 2 ) :

f=lambda n,i=4:i*'1'and"%02x"%(n%256)+f(n>>8,i-1)

Probieren Sie es online aus!

-1 Byte dank @JonathanAllan

Joel
quelle
Ich würde sagen, rekursiv als Python 2-Eintrag einreichen :)
Jonathan Allan
f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)spart ein Byte :)
Jonathan Allan
@ JonathanAllan Danke. Ich bin nicht mit allen Python 2-Tricks vertraut und sehe nicht, wie sie kürzer gemacht werden können.
Joel
es funktioniert nicht, aber die 37 wird in py 2 nicht funktionieren
Jonathan Allan
Ja. Einige dieser integrierten Funktionen sind nur für Python-3 verfügbar.
Joel
6

R , 54 53 Bytes

format.hexmode(scan()%/%256^(0:3)%%256%*%256^(3:0),8)

Probieren Sie es online aus!

Jede Gruppe von 2 Zeichen ist tatsächlich die hexadezimale Darstellung einer Ziffer in der Basis 256. scan()%/%256^(0:3)%%256Konvertiert in eine Basis-256-Zahl mit 4 umgekehrten Ziffern, ...%*%256^(3:0)verbindet sie als einzelne Ganzzahl und format.hexmode(...,8)konvertiert diese Zahl in ihre hexadezimale Darstellung mit 8 Ziffern.

Robin Ryder
quelle
5

JavaScript (ES7),  59  57 Byte

String-Manipulation.

n=>(n+2**32).toString(16).match(/\B../g).reverse().join``

Probieren Sie es online aus!

Wie?

n+2320

(304767 + 2**32).toString(16) // --> '10004a67f'

Probieren Sie es online aus!

/\B../g1\B

'10004a67f'.match(/\B../g) // --> [ '00', '04', 'a6', '7f' ]

Probieren Sie es online aus!

Wir reverse()und join()um die letzte Saite zu bekommen.


JavaScript (ES6), 61 Byte

Rekursive Funktion.

f=(n,k=4)=>k?[(x=n&255)>>4&&'']+x.toString(16)+f(n>>8,k-1):''

Probieren Sie es online aus!

Arnauld
quelle
⭐ - Sie bekommen Stern für nette Antwort - Ich mag es, kurz, aber immer noch sauber und "menschlich redable" :)
Kamil Kiełczewski
5

C # (Visual C # Interactive Compiler) , 54 Byte

x=>$"{(x=x>>16|x<<16)>>8&16711935|(x&16711935)<<8:x8}"

4 Bytes dank @PeterCordes gespeichert

Probieren Sie es online aus!

Erläuterung

x=>                                                    //Lambda taking in an uint
     (x=x>>16|x<<16)                                   //Swap the first two and the last two bytes of the uint (0x7fa60400 -> 0x04007fa6)
                    >>8&16711935|(x&16711935)<<8       //Swap each pair of bytes in every group of 2 bytes (0x04007fa6 -> 0x0004a67f)
  $"{                                           :x8}"  //Format as hex string, padded with leading zeroes to length 8
Verkörperung der Unwissenheit
quelle
Können Sie die 4278255360Maskenkonstante auf 16711935( 0xff00ff) verkleinern, wenn Sie vor dem Maskieren verschieben? Oder kostet das extra Parens? Wenn nicht, dann 0xff00ff00ist es gleich lang, aber für den Menschen viel bedeutungsvoller.
Peter Cordes
@PeterCordes Es hat auch den zusätzlichen Vorteil, dass die Klammern entfernt werden können, da es >>eine höhere Priorität hat als das &, was insgesamt 4 Bytes gespart hat. Vielen Dank!
Verkörperung der Unwissenheit
Cool. In Ihrem Abschnitt "Erklärung" würde ich vorschlagen, die Konstanten in hexadezimaler Form zu schreiben.
Peter Cordes
4

Japt -P , 10 Bytes

sG ùT8 ò w

Versuch es

sG ùT8 ò w     :Implicit input of integer
s              :Convert to string
 G             :  In base-16
   ù           :Left pad
    T          :  With 0
     8         :  To length 8
       ò       :Split into 2s
         w     :Reverse
               :Implicitly join and output
Zottelig
quelle
Was macht -Pdas
SS Anne
🚀 Ihre Antwort ist oben (können Sie eine Erklärung hinzufügen?)
Kamil Kiełczewski
@ JL2210 Aus den Dokumenten : " -P: Wenn die Ausgabe ein Array ist, werden Ausgaben ohne Trennzeichen (dh verbunden mit P) ausgegeben . " Das Flag steht also für einen impliziten statt für einen expliziten Join, um Bytes zu speichern. :)
Kevin Cruijssen
2
@ KamilKiełczewski, Erklärung hinzugefügt.
Shaggy
4

C (gcc) , 30 Bytes

f(x){printf("%.8x",htonl(x));}

Probieren Sie es online aus!

SS Anne
quelle
Wenn es auf einem Big-Endian-Computer ausgeführt wird, wird dies nicht in Little-Endian konvertiert?
Peter Ferrie
@ Peterferrie Siehe Revision 3.
SS Anne
4

Python 2 , 43 Bytes

lambda n:[("%08x"%n)[i^6]for i in range(8)]

Probieren Sie es online aus!

-4 Bytes dank benrg

Gibt eine Liste von Zeichen aus. Berechnet durch Abrufen der hexadezimalen Ziffern der Eingabe an Indizes 6, 7, 4, 5, 2, 3, 0, 1.

negative sieben
quelle
2
[i^6]for i in range(8)spart ein paar Bytes.
Benrg
Darf eine Liste anstelle einer Zeichenfolge ausgegeben werden?
Qwertiy
Ausgabe als Liste ist nicht wirklich geeignet für den Geist der Frage imo
qwr
3

C (gcc) endian agnostisch, keine Standardbibliotheken, 92 91 Bytes

h(n)ist eine einstellige Ganzzahl-> Hex-Hilfsfunktion.
f(x,p)nimmt eine ganze Zahl und einen char[8]Zeiger. Das Ergebnis sind 8 Datenbytes char. ( Nicht 0-terminiert, es sei denn, der Anrufer tut dies.)

Annahmen: ASCII-Zeichensatz. Das 2er-Komplement, intso dass die Rechtsverschiebung schließlich das Vorzeichenbit verringert , und das Konvertieren von a uint32_tin intdas Bitmuster wird nicht beeinträchtigt, wenn das hohe Bit gesetzt ist. intist mindestens 32-Bit. (Weiter kann es bei 1-Komplement- oder C-Implementierungen mit Vorzeichengröße funktionieren).

Nichtannahmen: Alles über die Reihenfolge der Implementierung oder die Signatur von char.

i;h(n){n&=15;return n>9?n+87:n+48;}f(x,p)char*p;{for(i=5;--i;x>>=8)*p++=h(x>>4),*p++=h(x);}

Probieren Sie es online aus! einschließlich Testaufrufer printf("%.8s\n", buf)zum Drucken des Ausgabepuffers ohne 0-Terminierung.

Ungolfed:

int h(n){n&=15;return n>9 ? n+'a'-10 : n+'0';}      // single digit integer -> hex

int i;
void ungolfed_f(x,p)char*p;{
    for(i=5; --i; x>>=8)   // LS byte first across bytes
        *p++=h(x>>4),      // MS nibble first within bytes
        *p++=h(x);
}

Doing n&=15;innen h(x)ist der Break-even; 6 Bytes dort gegen jeweils 3 &15, um das niedrige Knabbern an beiden Anrufstellen zu isolieren.

,ist ein Sequenzpunkt (oder ein Äquivalent in der modernen Terminologie), daher ist es sicher, *p++= stuffzweimal in einer Anweisung zu arbeiten, wenn dies vom ,Operator getrennt wird .

>>Eine vorzeichenbehaftete Ganzzahl wird durch die Implementierung entweder als arithmetisch oder als logisch definiert. GNU C definiert es als das Komplement der arithmetischen 2. Aber auf jeder 2er-Komplement-Maschine spielt es keine Rolle, weil wir niemals die eingeschobenen Nullen oder Kopien des Vorzeichenbits betrachten. Das ursprüngliche MSB wird schließlich unverändert in das Low-Byte gelangen. Dies ist bei Vorzeichen / Größe nicht der Fall, und ich bin mir über das Komplement von 1 nicht sicher.

Dies kann also möglicherweise nur auf 2-Komplement-C-Implementierungen portierbar sein. (Oder wo intes breiter als 32 Bit ist, so dass Bit 31 nur ein Teil der Größe ist.) Vorzeichenlose -> vorzeichenbehaftete Konvertierung mungiert auch das Bitmuster für negative ganze Zahlen, so dass &15ein intnur Halbbytes des ursprünglichen vorzeichenlosen Werts auf dem 2er-Komplement extrahiert. Wiederum, es intsei denn, es war breiter als 32-Bit, sodass alle Eingänge nicht negativ sind.

Bei der Golfversion fällt UB vom Ende einer nicht leeren Funktion ab. Einen Wert nicht zurückzugeben, nur um zu vermeiden, dass er voidanstelle des Standardwerts deklariert wird int. Moderne Compiler werden dies mit aktivierter Optimierung unterbrechen.


Motivation: Ich habe über eine x86- oder ARM Thumb asm-Antwort nachgedacht und dachte, es könnte Spaß machen, sie manuell in C auszuführen, möglicherweise für vom Compiler generierten asm als Ausgangspunkt. Unter /programming/53823756/how-to-convert-a-number-to-hex finden Sie Informationen zu geschwindigkeitseffizientem x86-ASM, einschließlich einer AVX512VBMI-Version, die nur zwei Anweisungen enthält (jedoch Kontrollvektoren für vpmultishiftqb und vpshufb benötigt) wäre also nicht toll für golf). Normalerweise erfordert SIMD zusätzliche Arbeit, um die Byte-Umkehrung in die Druckreihenfolge auf Little-Endian x86 durchzuführen, sodass diese byteumgekehrte Hex-Ausgabe tatsächlich einfacher als normal ist.


Andere Ideen

Ich habe überlegt, die Ganzzahl als Referenz zu nehmen und ihre Bytes mit char*einer Little-Endian-C-Implementierung (wie x86 oder ARM) zu durchlaufen. Aber ich denke nicht, dass das viel gespart hätte.

Verwenden sprintfSie jeweils 1 Byte, 64 Byte nach dem Golfen:

int i;
void f(x,p)char*p;{
        for(i=4;sprintf(p,"%.2x",x&255),--i;x>>=8)
                p+=2;
}

Wenn wir jedoch printf-ähnliche Funktionen verwenden, können wir genauso gut einen Byte-Swap durchführen und einen %xprintf des Ganzen wie die Antwort von @ JL2210 erstellen .

Peter Cordes
quelle
⭐ - Sie bekommen Stern für nette Antwort
Kamil Kiełczewski
3

x86 SIMD-Maschinencode (AVX512-VBMI), 36 Byte

(16 Bytes davon sind eine Hex-Nachschlagetabelle)

Dies ist eine Funktion, die eine Ganzzahl aufnimmt xmm0und 8 Byte ASCII-Zeichendaten zurückgibt xmm0, damit der Aufrufer speichern kann, wo immer er will. (zB in den Videospeicher nach dem Verschachteln mit Attributbytes oder in eine im Aufbau befindliche Zeichenfolge oder was auch immer)

Rufen Sie es in C wie __m128i retval = lehex(_mm_cvtsi32_si128(x))bei der x86-64-System V-Aufrufkonvention oder bei MS Windows auf vectorcall.

# disassembly with machine-code bytes (the answer) and NASM source code.
0000000000401000 <lehex>:
  401000:       c5 f1 72 d0 04          vpsrld      xmm1, xmm0, 4         ; AVX1
  401005:       c5 f1 60 c8             vpunpcklbw  xmm1, xmm1, xmm0      ; AVX1
  401009:    62 f2 75 08 8d 05 01 00 00 00 vpermb  xmm0, xmm1, [rel .hex_lut]
  401013:       c3                      ret    

0000000000401014 <lehex.hex_lut>:
  401014:     30 31 ...  61 62 ...     .hex_lut:  db "0123456789abcdef"

Gesamt = 0x24 = 36 Bytes.

Siehe Wie konvertiere ich eine Zahl in Hex? auf SO, wie das funktioniert. (SSE2 für Shift / Punpck vpermbspart dann die Arbeit, die wir benötigen würden pshufb. AVX1 anstelle von SSE2 / SSSE3 vermeidet auch eine movapsRegisterkopie.)

Beachten Sie, dass punpcklbwmit den Quelloperanden in dieser Reihenfolge das höchstwertige Halbbyte des unteren Eingabebytes im Element mit dem niedrigsten Byte und dann das niedrigstwertige Halbbyte des niedrigsten Quellbytes erhalten wird. (In dieser SO-Antwort wird a bswapfür die Eingabe verwendet, um ein Ergebnis in der Standarddruckreihenfolge mit nur SSE2 zu erhalten. Aber hier wollen wir diese Reihenfolge: hohes Halbbyte im unteren Element innerhalb jedes Bytes, aber immer noch Little-Endian-Bytereihenfolge).

Wenn wir mehr Daten Konstanten hätten, könnten wir Adressieren-Modus Platz sparen , indem man tut mov edx, imm32dann mit [rdx+16]oder was auch immer Adressierungsarten. Oder vpbroadcastb xmm0, [rdx+1].

Ich denke jedoch, dass eine 16-Byte-Hex-LUT + vpermbimmer noch besser ist als die Implementierung der n>9 : n+'a'-10 : n+'0'Bedingung: Dies erfordert 3 Konstanten und mindestens 3 Anweisungen mit AVX512BW-Bytemaskierung (vergleiche in mask ,, vpaddbmerge- vpaddbmasked) oder mehr mit AVX1 oder SSE2. (Siehe So konvertieren Sie eine Zahl in Hex? Auf SO für eine SSE2-Version davon). Und jeder AVX512BW-Befehl ist mindestens 6 Byte lang (4 Byte EVEX + Opcode + Modrm), länger mit einer Verschiebung im Adressierungsmodus.

Tatsächlich würde es mindestens 4 Anweisungen erfordern, da wir vor dem Vergleich hohen Müll mit andps(oder EVEX vpanddmit einem 4-Byte-Broadcast-Speicheroperanden) löschen müssen . Und jede davon benötigt eine andere Vektorkonstante. AVX512 verfügt über Broadcast-Speicheroperanden, jedoch nur für Elemente mit 32 Bit und mehr. zB EVEXvpaddb ‚s letzte Operand ist nur xmm3/m128, nicht xmm3/m128/m8bcst. (Intels Load-Ports können nur 32- und 64-Bit-Broadcasts im Rahmen eines Load-Uops kostenlos ausführen. Intel hat AVX512BW so konzipiert, dass dies nicht berücksichtigt wird, und kann überhaupt keine Byte- oder Word-Broadcast-Speicheroperanden codieren, anstatt ihnen die Option zu geben Führen Sie Dword-Broadcasts durch, damit Sie Ihre Konstanten weiterhin auf 4 Byte komprimieren können: /.)

Ich habe AVX512VBMIvpermb anstelle von SSSE3 / AVX1 pshufbin zweierlei Hinsicht verwendet :

  • vpermbignoriert hohe Bits der Selektoren. (v)pshufbNull-Bytes gemäß dem hohen Bit des Steuervektors und hätte ein zusätzliches pandoder andpstatsächlich isoliertes Halbbyte benötigt. Bei einer XMM / 16-Byte-Größe werden vpermbnur die niedrigen 4 Bits der Shuffle-Steuerelemente betrachtet, dh die Bits [3:0]in der Intel-Notation im Abschnitt Operation .
  • vpermbkann die zu mischenden Daten (die Nachschlagetabelle) als Speicheroperanden verwenden. (v)pshufbDer xmm / mem-Operand ist der Shuffle-Control-Vektor.

Beachten Sie, dass AVX512VBMI nur auf CannonLake / Ice Lake verfügbar ist. Daher benötigen Sie wahrscheinlich einen Simulator, um dies zu testen, wie z. B. Intels SDE.

Peter Cordes
quelle
⭐ - Sie bekommen Stern für nette Antwort
Kamil Kiełczewski
@ KamilKiełczewski: lol danke. Zahlen effizient in Hex umzuwandeln ist eine meiner Lieblingssachen. Es ist ein guter Anwendungsfall für einige nette Tricks und Bit-Manipulationen.
Peter Cordes
3

Scala , 58 40 36 Bytes

"%08X"format Integer.reverseBytes(_)

Probieren Sie es online aus!

Verwendet immer noch das eingebaute, um die Bytes von umzukehren Int, aber verwendet, formatum das Intals Hex zu formatieren . Sie müssen nicht anrufen toHexString.

Die Parens wurden entfernt format. Dies bedeutet nun, dass das Argument implizit mit verwendet werden kann _.

Seife
quelle
2

Forth (gforth) , 52 51 40 Bytes

: f hex 0 4. do <# # # 0. #> type loop ;

Probieren Sie es online aus!

Code-Erklärung

: f           \ start a new word definition
  hex         \ set the current base to base 16
  0           \ convert the input number to a double-cell integer
  4. do       \ start a counted loop from 0 to 3
    <# # #    \ start a formatted numeric string and move last 2 digits to format area
    0.        \ move remaining digits down the stack
    #>        \ delete top two stack value and convert format area to string
    type      \ output string
  loop        \ end loop
;             \ end word definition
Reffu
quelle
2

Gelee , 13 Bytes

+Ø%b⁴Ḋs2Ṛ‘ịØh

Probieren Sie es online aus!

Ein vollständiges Programm, das eine Ganzzahl als Argument verwendet und eine Zeichenfolge druckt.

Nick Kennedy
quelle
🚀 Ihre Antwort ist oben
Kamil Kiełczewski
2

Excel, 91 Bytes

=RIGHT(DEC2HEX(A1,8),2)&MID(DEC2HEX(A1,8),5,2)&MID(DEC2HEX(A1,8),3,2)&LEFT(DEC2HEX(A1,8),2)
Wernisch
quelle
2

K4 , 12 11 Bytes

Lösung:

,/$|4_0x0\:

Beispiele:

q)k),/$|4_0x0\:304767
"7fa60400"
q)0W
"0004a67f"

Erläuterung:

Ziemlich genau das, was die Frage stellt:

,/$|4_0x0\: / the solution
      0x0\: / split to bytes
    4_      / drop first 4 bytes
   |        / reverse
  $         / convert to string
,/          / flatten

Anmerkungen:

  • -1 Byte als K4-Zahlen sind standardmäßig Longs (64 Bit), daher werden 4 Bytes (32 Bit) gelöscht.
Streetster
quelle
🚀 Ihre Antwort ist oben
Kamil Kiełczewski
2

PHP , 31 Bytes

<?=unpack(H8,pack(V,$argn))[1];

Probieren Sie es online aus!

Ich nutze das Packen und Entpacken von PHP , packe die vorzeichenlose Eingabe mit dem Format "32-Bit-Little-Endian-Byte-Reihenfolge" ( V) in eine Binärzeichenfolge und entpacke sie dann mit dem Format "Hex-Zeichenfolge, High Nibble First" ( H) und drucke das Ergebnis.

Dies scheint einer der seltenen Fälle zu sein, in denen die integrierten Funktionen von PHP tatsächlich kürzer sind als die Implementierung eines einfachen Algorithmus!

Nacht2
quelle
PHPs pack()/ unpack()Funktionen sind fantastisch für das 0-fache, das Sie jemals in den meisten PHP-Projekten benötigen. Herzlichen Glückwunsch, Sie haben ihre Verwendung gefunden!
640 KB
1

Holzkohle , 11 Bytes

⪫⮌⪪﹪%08xN²ω

Probieren Sie es online aus! Der Link führt zur ausführlichen Version des Codes. Erläuterung:

        N   Input as a number
   ﹪%08x    Format using literal string
  ⪪      ²  Split into pairs of characters
 ⮌          Reverse
⪫         ω Join
            Implicitly print

19 Bytes ohne Python-Formatierung:

⪫…⮌⪪⍘⁺X²¦³⁶N¹⁶¦²¦⁴ω

Probieren Sie es online aus! Der Link führt zur ausführlichen Version des Codes. Erläuterung:

           N        Input as a number
     ⁺              Plus
       ²            Literal 2
      X             To power
         ³⁶         Literal 36
    ⍘               Convert to base
            ¹⁶      Literal 16
   ⪪           ²    Split into pairs of digits
  ⮌                 Reverse the list
 …               ⁴  Take the first 4 pairs
⪫                 ω Join together
                    Implicitly print
Neil
quelle
🚀 Ihre Antwort ist oben
Kamil Kiełczewski
1

J , 10 Bytes

8{._1{3!:3

Probieren Sie es online aus!

Wie

3!:3ist eine J "Fremdkonjunktion" für die Hex-Darstellung, die hier dokumentiert ist . Das heißt, es ist eine integrierte Funktion zum Konvertieren in Hex. Es ist jedoch nicht ganz das, was wir wollen. ZB Laufen:

3!:3 (304767)

produziert:

e300000000000000
0400000000000000
0100000000000000
0000000000000000
7fa6040000000000

Die Bedeutung der anderen Zeilen wird auf der oben verlinkten Dokumentseite erläutert. Auf jeden Fall ist klar, dass wir die ersten 8 Zeichen der letzten Zeile wollen.

_1{ Holen Sie sich die letzte Zeile.

8{. bekommt die ersten 8 Zeichen davon.

Jona
quelle
🚀 Ihre Antwort ist oben
Kamil Kiełczewski
1

Windows Batch, 90 Bytes

@for /l %%x in (24,-8,0)do @set/aa=%1^>^>%%x^&255&cmd/cexit !a!&<nul set/p=!=exitcode:~-2!

Führen Sie die Befehlszeile mit / v aus, um die verzögerte Erweiterung zu aktivieren.

Peter Ferrie
quelle
1

x86 32-Bit-Maschinencode, 24 21 Byte

Changelog: -3 Bytes: Ersetzen Sie das Standard-Add / cmp / jbe / add durch einen DAS-Hack von @peter ferrie

64-Bit: immer noch 24 Bytes. Im Long-Modus wurde der DAS-Opcode entfernt.
16-Bit-Modus: Die Standardoperandengröße ist 16-Bit, aber die Problemspezifikation ist von Natur aus 32-Bit. Einschließlich fest codierter 8 hexadezimaler Ziffern.


Byte-Reverse mit bswapdann manuellem int-> hex in Standardreihenfolge (höchstwertiges Halbbyte zuerst, Schreiben von Hexadezimalziffern in aufsteigender Reihenfolge in einen Zeichenausgabepuffer ). Dadurch wird vermieden, dass die Schleife abgewickelt werden muss, um die Reihenfolge zwischen Halbbytes innerhalb eines Bytes zu wechseln. über Bytes.

Aufrufbar void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);wie x86-64 System V, außer dass dies im 64-Bit-Modus nicht funktioniert. (Es benötigt den Ausgabezeiger in EDI für stosb. Die Eingangsnummer kann sich in einem anderen Register als ECX oder EAX befinden.)

     1                             lehex:
     2 00000000 0FCE                   bswap  esi
     3 00000002 6A08                   push   8            ; 8 hex digits
     4 00000004 59                     pop    ecx
     5                             .loop:                ;do{
     6 00000005 C1C604                 rol    esi, 4       ; rotate high nibble to the bottom
     7                             
     8 00000008 89F0                   mov    eax, esi
     9 0000000A 240F                   and    al, 0x0f     ; isolate low nibble
    10 0000000C 3C0A                   cmp al, 10          ; set CF according to digit <= 9
    11 0000000E 1C69                   sbb al, 0x69        ; read CF, set CF and conditionally set AF
    12 00000010 2F                     das                 ; magic, which happens to work
    13                             
    14 00000011 AA                     stosb               ; *edi++ = al
    15 00000012 E2F1                   loop  .loop       ; }while(--ecx)
    16                             
    17 00000014 C3                     ret

Größe = 0x15 = 21 Bytes.

TIO FASM 32-Bit x86-Testfall mit einem asm-Aufrufer, der einen writeSystemaufruf verwendet, um die Ausgabe zu schreiben, nachdem er zweimal aufgerufen wurde, um 2 Zeichenfolgen an einen Puffer anzuhängen. Testet alle hexadezimalen Ziffern 0..F, einschließlich 9 und A an der Grenze zwischen Ziffer und Buchstabe.

Der DASHack - x86 hat eine Half-Carry-Flagge zum Mitnehmen aus dem Low-Nibble. Nützlich für gepackte BCD-Inhalte wie den DAS-Befehl, der nach dem Subtrahieren von zwei zweistelligen BCD-Ganzzahlen verwendet werden soll. Da das niedrige Knabbern von AL außerhalb des Bereichs von 0 bis 9 liegt, missbrauchen wir es hier definitiv.

Beachten Sie den if (old_AL > 99H) or (old_CF = 1)DANN- AL ← AL − 60H;Teil des Bedienungsabschnitts im Handbuch. sbb setzt hier immer CF, so dass ein Teil immer passiert. Das und der ASCII-Bereich für Großbuchstaben motivieren die Wahl vonsub al, 0x69

  • cmp 0xD, 0xA setzt CF nicht
  • sbb wird als Eingabe in DAS 0xD - 0x69in AL = umgebrochen 0xA4. (Und setzt CF, löscht AF)
  • nein AL - = 6 im ersten Teil von DAS (weil 4> 9 falsch ist und AF = 0)
  • AL - = 0x60 im zweiten Teil, wobei 0x44der ASCII-Code für übrig bleibt'D'

gegen eine Ziffer:

  • cmp 0x3, 0xA setzt CF
  • sbb 3 - 0x69 - 1= AL = 0x99 und setzt CF und AF
  • nein AL - = 6 im ersten Teil von DAS (9> 9 ist falsch, aber AF ist gesetzt), so dass 0x93 übrig bleibt
  • AL - = 0x60 im zweiten Teil, wobei 0x33, der ASCII-Code für '3'.

Durch Subtrahieren 0x6ain der SBB wird AF für jede Ziffer <= 9 eingestellt, sodass alle Ziffern der gleichen Logik folgen. Und lassen Sie es für jede alphabetische Hex-Ziffer frei. dh die 9 / A-Split-Behandlung von DAS korrekt ausnutzen.


Normalerweise (aus Gründen der Leistung) verwenden Sie eine Nachschlagetabelle für eine Skalarschleife oder möglicherweise eine verzweigungslose 2x- leaund cmp/cmovbedingte Addition. Aber 2-Byte- al, imm8Anweisungen sind ein großer Gewinn für die Codegröße.


x86-64 version version : nur der Teil, der sich zwischen and al, 0xfund unterscheidet stosb.

;; x86-64 int -> hex  in 8 bytes
    10 0000000C 0430                   add    al, '0'
    11 0000000E 3C39                   cmp    al, '9'
    12 00000010 7602                   jbe  .digit
    13 00000012 0427                     add    al, 'a'-10 - '0'     ; al =  al>9 ? al+'a'-10 : al+'0'
    14                             .digit:

Beachten Sie, dass das add al, '0' immer ausgeführt wird und das bedingte Hinzufügen nur den Unterschied zwischen 'a'-10und hinzufügt '0', um es nur zu einem ifanstelle von if/ zu machen else.

Getestet und funktioniert mit demselben mainAnrufer wie meine C-Antwort , die char buf[8]und verwendet printf("%.8s\n", buf).

Peter Cordes
quelle
Kannst du zB hier ein online arbeitendes Snippet erstellen ?
Kamil Kiełczewski
@ KamilKiełczewski: TIO macht es unmöglich (AFAIK), den Aufrufer in C zu schreiben, um eine ASM-Funktion zu testen, so dass ich mich oft nicht darum kümmere, aber sicher, da Sie gefragt haben und sys_writeZeichenfolgen mit fester Länge einfach ausgeben können. Oh interessant, ich hatte nicht bemerkt, dass FASM auf TIO Sie 32-Bit-ausführbare Dateien erstellen lässt, im Gegensatz zu NASM, wo es nicht respektiert -felf32. Ich bevorzuge sowieso x86-64, und diese Antwort speichert keine Bytes aus 32-Bit-Code.
Peter Cordes
⭐ - Sie bekommen Stern für nette Antwort
Kamil Kiełczewski
1
@ JL2210: Meinst du sprintf? Ich glaube nicht, dass libc andere nützliche int-> string-Funktionen als format-stringbasierte hat, nur string-> int wie strtoul. Aber ja, bswap / printf wäre wahrscheinlich kürzer, wenn Sie herausfinden könnten, wie Bytes für den GOT-Eintrag für eine Funktion in einer dynamischen Bibliothek gezählt werden können (neben der 6-Byte- call [rel printf wrt ..got]Aufrufstelle). Eine minimale Anzahl statisch verknüpfter ausführbarer Dateien kann erheblich kleiner als dynamisch sein, zumindest wenn sie ldmit normalen Standardeinstellungen erstellt wurden. Aber ich denke nicht, dass es vernünftig wäre, es statisch zu verknüpfen, aber seine Codegröße nicht zu zählen.
Peter Cordes
1
@ JL2210: Denken Sie daran, dies ist eine x86- Maschinencode- Antwort, keine asm-Textquellengröße. Ich habe in früheren Maschinencode-Antworten keine libc-Funktionen verwendet, nur Linux-Systemaufrufe (z. B. in Fibonacci) und IDK, wie ich die Kosten zählen würde oder ob ich überhaupt Maschinencode-mit-libc-Antworten schreiben möchte . Es gibt Anwendungsfälle für x86-Maschinencode, in denen keine libc verfügbar ist, z. B. in einem Bootloader.
Peter Cordes