Seite 1 von 1
Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 13:36
von may24x
Hi zusammen,
ich bastle gerade einen eigenen Binär-Search.
D.h. ich habe ein Such-Pattern was ich mit meinem Buffer vergleichen will.
Alle Werte sind "eigentlich" (aber was ist schon eigentlich ...) Bytes und keine Char's, aber wie mache ich hier eine Byte-weise XOR Operation ?
Code: Alles auswählen
...
seek_pointer = 0
bin_search_pattern = b'\x00\x00\x00\x01\x42'
with open(inputfile, "br") as fi:
while True:
read_buffer = fi.read(5)
test = read_buffer ^ bin_search_pattern
if test:
print(seek_pointer)
seek_pointer += 1
fi.seek(seek_pointer)
Code: Alles auswählen
test = read_buffer ^ bin_search_pattern
TypeError: unsupported operand type(s) for ^: 'bytes' and 'bytes'
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 13:43
von BlackJack
@may24x: Was soll denn ``if test:`` hier bedeuten? Jedes `bytes`-Objekt mit einer Länge von mindestens 1 ist ”wahr”, die Bedingung ist also etwas komisch.
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 14:20
von may24x
oops, stimmt ... das stammt noch von vorherigem Code ... da war/sollte test ein Bool sein ...
Aber egal erst mal, das hier geht schon nicht
Code: Alles auswählen
bin_search_pattern = b'\x00\x00\x00\x01\x42'
seek_pointer = 0
with open(inputfile, "br") as fi:
while True:
read_buffer = fi.read(5)
test = read_buffer ^ bin_search_pattern
print(test)
PS: Warum funktioniert der Codeblocks Tag eigentlich nicht ?
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 14:37
von BlackJack
@may24x: Ja aber was sollte `test` als `bool` an der Stelle aussagen? Was möchtest Du denn da machen? Was sollte bei ``read_buffer ^ bin_search_pattern`` heraus kommen und auf was soll dieses Ergebnis dann getestet werden? (Ich will hier sicherstellen das Du nicht einfach nur einen simplen Vergleich viel zu kompliziert ausdrücken willst.)
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 15:13
von may24x
Nun, ich habe zwei Array's mit Bytes. Eines was mein Search-Pattern ausdrückt und einen Ausschnitt welches ich binär aus einer Datei lese.
Um zu prüfen ob der Ausschnitt mit meiner Suche übereinstimmt, benutze ich eine EXOR Verknüpfung.
D.h. sind beide gleich - binär gesehen - müsste "test" -> \xff \xff \xff \xff \xff -> True sein
Die "if" Abfrage gibt mir dann die Position aus wenn die zwei Byte-folgen überein stimmen
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 15:24
von BlackJack
@may24x: Genau so etwas hatte ich befürchtet. *WARUM*‽ Mach einfach einen Vergleich und gut ist. Zudem ist das Ergebnis lauter 0-Bits wenn die beiden Bytefolgen gleich sind.
Du willst also ``test = read_buffer == bin_search_pattern``. Und *eigentlich* willst Du in grösseren Blöcken lesen und die `index()`-Methode zum Suchen verwenden, kein `seek()` verwenden, und vielleicht sogar einfach das `mmap`-Modul benutzen.
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 15:40
von may24x
Tja ... das mit dem XOR war zu kompliziert gedacht
ein einfaches "==" funktioniert wunderbar.
Nein, das mit den größeren Blöcken ist so 'ne Sache. Da die Files die ich parse zwischen 500MB und 2 GB groß sind, müsste ich das Ganze erst mal in den Speicher schieben, dann indexieren und durchsuchen ...
Da ist meine Vorgehensweise doch etwas schlanker ... wenn auch nicht so performant ... behaupte ich mal
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 15:58
von BlackJack
@may24x: Es ist halt (abgesehen von komischen Sachen wie XOR

) die langsamste Art das zu machen, und das dann auch noch wenn die Datenmengen grösser sind. Du solltest ja nicht alles auf einmal in den Speicher laden, aber mindestens mal grössere Blöcke verarbeiten. Oder eben `mmap` anschauen.
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 16:20
von may24x
Hm ... ok ... Ich hab 'n ARM7 Prozessor mit 512 MB Speicher ... insgesamt.
D.h. für OS + App's ... ist kein Handy sondern ein SAP (Stand Alone Player).
Über'n Daumen schätze ich mal das ich ca. 100 MB für das Script kurzzeitig mir unter'n Nagel reißen könnte.
Was meinst du ... index() oder mmap ... ?
Und wenn der schon eine Binärsuche macht ... wie macht er das ? Schiebt Python das Pattern immer nur ein Byte weiter und testet ... oder mehrere ... ganz anders ?
Re: Byte-weise XOR
Verfasst: Dienstag 6. Juni 2017, 16:31
von BlackJack
@may24x: Hm, beim ARM wüsste ich jetzt gerade nicht ob der trotzdem einen grossen, virtuellen Adressraum zur Verfügung stellen kann, deshalb könnte `mmap` für wirklich grosse Dateien nicht funktionieren.
100 MiB würde ich wohl auch nicht gleich nehmen, eher etwas zwischen 16 Kilobyte und einem Megabyte. Auf jeden Fall würde ich Daten nicht in len(pattern)-Häppchen und dann auch noch len(pattern) mal lesen was Du ja effektiv machst.
Re: Byte-weise XOR
Verfasst: Mittwoch 7. Juni 2017, 01:08
von may24x
Nun das mit dem index() scheint gar keine so gute Idee zu sein, da nur das erste Element ausgegeben wird.
Kommt aber die Search-Pattern mehrfach vor, werden alle folgenden ignoriert ....

Re: Byte-weise XOR
Verfasst: Mittwoch 7. Juni 2017, 06:59
von snafu
may24x hat geschrieben:Nun das mit dem index() scheint gar keine so gute Idee zu sein, da nur das erste Element ausgegeben wird.
Kommt aber die Search-Pattern mehrfach vor, werden alle folgenden ignoriert ....

Man kann festlegen, dass index() bei einer Position > 0 mit der Suche beginnt. Wenn man das so implementiert, dass auch abgeschnittene Treffer (also zwischen zwei Chunks) erkannt werden, dann wird es schon etwas komplexer, ist aber machbar:
Code: Alles auswählen
def get_chunks(f, size=1024):
while True:
chunk = f.read(size)
if not chunk:
break
yield chunk
def find_indices(chunk, needle, index=0):
while True:
try:
index = chunk.index(needle, index)
except ValueError:
break
yield index
index += len(needle)
def find_all(filename, needle, is_binary=False):
mode = 'rb' if is_binary else 'r'
with open(filename, mode) as infile:
buf = ''
for chunk in get_chunks(infile):
for index in find_indices(buf + chunk, needle):
yield index
if not chunk.endswith(needle):
# Might be part of truncated needle
buf = chunk[-(len(needle) - 1):]
Re: Byte-weise XOR
Verfasst: Mittwoch 7. Juni 2017, 08:51
von Sirius3
@snafu: buf ist falsch, wenn Chunk 17 nicht mit needle ended, Chunk 18 aber schon; die Frage ist ja auch, ob es überlappende needels geben könnte. Der Index berücksichtigt nicht, dass schon mehrere Chunks gelesen wurde und liefert nur relative Werte. Für len(needle)==1 funktioniert das Slicing nicht.
Code: Alles auswählen
def find_all(filename, needle, mode = 'rb'):
start = 0
with open(filename, mode) as infile:
buf = ''
while True:
data = infile.read(1024)
if not data:
break
start += len(buf)
if len(needle)>1:
buf = buf[-(len(needle)-1):]
start -= len(buf)
buf += data
index = 0
while True:
try:
index = chunk.index(needle, index)
except ValueError:
break
yield start + index
index += len(needle)
oder mit mmap
Code: Alles auswählen
import mmap
def find_all(filename, needle):
with open(filename, 'rb') as infile:
data = mmap(infile.fileno(), 0, access=mmap.ACCESS_READ)
index = 0
while True:
index = data.find(needle, index)
if index < 0:
break
yield index
index += len(needle)
Re: Byte-weise XOR
Verfasst: Mittwoch 7. Juni 2017, 08:57
von snafu
Die Indexberechnung über mehrere Chunks war vermurkst. Ist nun korrigiert:
Code: Alles auswählen
CHUNK_SIZE = 1024 * 1024
def get_chunks(f, size=CHUNK_SIZE):
while True:
chunk = f.read(size)
if not chunk:
break
yield chunk
def find_indices(chunk, needle, index=0):
while True:
try:
index = chunk.index(needle, index)
except ValueError:
break
yield index
index += len(needle)
def find_all(filename, needle, is_binary=False):
mode = 'rb' if is_binary else 'r'
with open(filename, mode) as infile:
buf = ''
index_total = 0
for chunk in get_chunks(infile):
for index in find_indices(buf + chunk, needle):
yield index_total + index
if chunk.endswith(needle) or len(needle) <= 1:
buf = ''
else:
# Might be part of truncated needle
buf = chunk[-(len(needle) - 1):]
index_total += len(chunk) - len(buf)
Re: Byte-weise XOR
Verfasst: Mittwoch 7. Juni 2017, 09:16
von snafu
@Sirius3:
Danke für deine Anmerkungen.
Ob mmap genutzt werden kann, ist hier ja nicht sicher.
Das Beachten von überlappenden Needles kommt in deinem Code aber auch nicht vor, oder übersehe ich was?
Zumal das deutlich aufwändiger und womöglich unnötig wäre.
Re: Byte-weise XOR
Verfasst: Mittwoch 7. Juni 2017, 10:20
von Sirius3
Mein Test mit mmap auf einem Rechner mit 32GB und einer Datei mit 100GB hat gezeigt, dass das Programm nur 10GB für sich verwendet hat. Sollte so auf einen ARMv7 mit 512MB RAM übertragbar sein.
Re: Byte-weise XOR
Verfasst: Mittwoch 7. Juni 2017, 10:28
von BlackJack
@Sirius3: Was zum Problem werden könnte ist der Adressraum, denn die Datei wird ja in den Adressraum des Prozesses abgebildet, und wenn der beispielsweise bei einem 32-Bit-System nur 2GiB gross ist, dann bekommt man da zusätzlich zu Programm und Daten keine 2GiB grosse Datei mehr abgebildet.
Der ARM im Raspi 3 ist beispielsweise ein 64-Bit-Prozessor, aber Rasbian ist trotzdem nur ein 32-Bit-System. Die haben abgewogen zwischen Geschwindigkeitsgewinn (war wohl nicht so doll) und dem verhältnismässig kleinen Speicher (1 GiB RAM), für den 32-Bit zum adressieren ausreichen, und dem zusätzlichen Speicherverbrauch von 64-Bit grossen Zeigern.