Wprowadzenie
Jesteś prawdopodobnie zna bomby zip , bomb XML itp Mówiąc prościej, są (względnie) to małe pliki, które produkują ogromne wyjście kiedy interpretowane przez naiwnego oprogramowania. Wyzwaniem jest nadużycie kompilatora w ten sam sposób.
Wyzwanie
Napisz kod źródłowy, który zajmuje 512 bajtów lub mniej i który kompiluje się w plik, który zajmuje najwięcej możliwej przestrzeni. Największy plik wyjściowy wygrywa!
Zasady
OK, więc jest kilka ważnych wyjaśnień, definicji i ograniczeń;
- Dane wyjściowe kompilacji muszą być plikiem ELF , przenośnym plikiem wykonywalnym systemu Windows (.exe) lub wirtualnym kodem bajtowym dla JVM lub CLR .Net (inne typy wirtualnego kodu bajtowego również prawdopodobnie będą OK, jeśli zostaną o to poproszone). Aktualizacja: Dane wyjściowe .pyc / .pyo Pythona również się liczą .
- Jeśli wybranego języka nie można skompilować bezpośrednio w jednym z tych formatów, dozwolona jest również transpilacja, a następnie kompilacja ( Aktualizacja: możesz transpilować wiele razy, o ile nigdy nie używasz tego samego języka więcej niż jeden raz ).
- Kod źródłowy może składać się z wielu plików, a nawet plików zasobów, ale suma wszystkich tych plików nie może przekraczać 512 bajtów.
- Nie można używać innych danych wejściowych niż pliki źródłowe i standardowa biblioteka wybranego języka. Łączenie statyczne bibliotek standardowych jest OK, jeśli jest obsługiwane. W szczególności nie ma bibliotek stron trzecich ani bibliotek systemu operacyjnego.
- Musi istnieć możliwość wywołania kompilacji za pomocą polecenia lub szeregu poleceń. Jeśli potrzebujesz specyficznych flag podczas kompilacji, liczą się one do limitu bajtów (np. Jeśli twoja linia kompilacji jest
gcc bomb.c -o bomb -O3 -lm
,-O3 -lm
część (7 bajtów) zostanie policzona (zwróć uwagę, że początkowa spacja nie jest liczona). - Preprocesory są dozwolone tylko wtedy, gdy są standardową opcją kompilacji dla twojego języka.
- Środowisko zależy od ciebie, ale aby uczynić to weryfikowalnym, trzymaj się najnowszych (tj. Dostępnych) wersji kompilatora i systemów operacyjnych (i oczywiście określ, którego używasz).
- Musi się kompilować bez błędów (ostrzeżenia są OK), a awaria kompilatora nie ma znaczenia.
- To, co faktycznie robi twój program , jest nieistotne, chociaż nie może być niczym złośliwym. Nie musi nawet być w stanie się uruchomić.
Przykład 1
Program C.
main(){return 1;}
Kompilowany z Apple LLVM version 7.0.2 (clang-700.1.81)
OS X 10.11 (64-bit):
clang bomb.c -o bomb -pg
Tworzy plik 9228 bajtów. Całkowity rozmiar źródła to 17 + 3 (dla -pg
) = 20 bajtów, co łatwo mieści się w limicie rozmiaru.
Przykład 2
Program Brainfuck:
++++++[->++++++++++++<]>.----[--<+++>]<-.+++++++..+++.[--->+<]>-----.--
-[-<+++>]<.---[--->++++<]>-.+++.------.--------.-[---<+>]<.[--->+<]>-.
Przeniesione z awib do c z:
./awib < bomb.bf > bomb.c
Następnie skompilowany z Apple LLVM version 7.0.2 (clang-700.1.81)
OS X 10.11 (64-bit):
clang bomb.c
Tworzy plik 8464 bajtów. Łączna ilość danych wejściowych wynosi 143 bajty (ponieważ @lang_c
jest domyślną opcją awib, nie trzeba jej dodawać do pliku źródłowego, a żadne polecenie nie ma specjalnych flag).
Należy również pamiętać, że w tym przypadku plik tymczasowy bomb.c ma 802 bajtów, ale nie ma to wpływu na rozmiar źródła ani rozmiar wyjściowy.
Ostatnia uwaga
Jeśli zostanie uzyskana moc wyjściowa większa niż 4 GB (być może jeśli ktoś znajdzie kompletny preprocesor Turinga), konkurencja będzie dotyczyć najmniejszego źródła, które tworzy plik o co najmniej takiej wielkości (po prostu nie jest praktyczne testowanie zgłoszeń, które stają się zbyt duże) .
Odpowiedzi:
C, (14 + 15) = źródło 29 bajtów, plik wykonywalny 17 179 875 837 (16 GB)
Dzięki @viraptor za 6 bajtów off.
Dzięki @hvd za 2 bajty wyłączone i rozmiar pliku wykonywalnego x4.
Definiuje to
main
funkcję jako dużą tablicę i inicjuje jej pierwszy element. To powoduje, że GCC przechowuje całą tablicę w wynikowym pliku wykonywalnym.Ponieważ ta tablica jest większa niż 2 GB, musimy dostarczyć
-mcmodel=medium
flagę do GCC. Dodatkowe 15 bajtów jest uwzględnionych w wyniku, zgodnie z zasadami.Nie oczekuj, że ten kod zrobi coś miłego po uruchomieniu.
Połącz z:
Zajęło mi trochę czasu, aby przejść do testowania sugestii @ hvd - i znaleźć maszynę z wystarczającą ilością soku, aby sobie z tym poradzić. W końcu znalazłem starą nieprodukcyjną maszynę RedHat 5.6 z 10 GB pamięci RAM, 12 GB wymiany i / tmp ustawioną na dużą partycję lokalną. Wersja GCC to 4.1.2. Całkowity czas kompilacji około 27 minut.
źródło
a
. Możesz po prostu użyćmain[1<<30]={1};
1<<30
wtedy,7<<28
może być opcją.C #, około 1 min do skompilowania, wyjście binarne 28 MB:
Dodanie większej liczby Y spowoduje wykładniczy wzrost rozmiaru.
Wyjaśnienie Pharap zgodnie z prośbą @Odomontois:
Ta odpowiedź nadużywa parametrów dziedziczenia i typu do tworzenia rekurencji. Aby zrozumieć, co się dzieje, łatwiej jest najpierw uprościć problem. Zastanów się
class X<A> { class Y : X<Y> { Y y; } }
, który generuje klasę ogólnąX<A>
, która ma klasę wewnętrznąY
.X<A>.Y
dziedziczyX<Y>
, stądX<A>.Y
też ma klasę wewnętrznąY
, która jest wtedyX<A>.Y.Y
. Ma to również klasę wewnętrznąY
, a ta klasa wewnętrznaY
ma klasę wewnętrznąY
itp. Oznacza to, że możesz używać scope resolution (.
) ad infinitum i za każdym razem, gdy go używasz, kompilator musi wydedukować inny poziom dziedziczenia i parametryzację typu .Dodając dodatkowe parametry typu, praca kompilatora na każdym etapie jest zwiększana.
Rozważ następujące przypadki:
W
class X<A> { class Y : X<Y> { Y y;} }
typie parametrA
ma typX<A>.Y
.W
class X<A> { class Y : X<Y> { Y.Y y;} }
typ paramA
ma typX<X<A>.Y>.Y
.W
class X<A> { class Y : X<Y> { Y.Y.Y y;} }
typ paramA
ma typX<X<X<A>.Y>.Y>.Y
.W
class X<A,B> { class Y : X<Y,Y> { Y y;} }
typie paramA
jestX<A,B>.Y
iB
jestX<A,B>.Y
.W
class X<A> { class Y : X<Y> { Y.Y y;} }
typie paramA
jestX<X<A,B>.Y, X<A,B>.Y>.Y
iB
jestX<X<A,B>.Y, X<A,B>.Y>.Y
.W
class X<A> { class Y : X<Y> { Y.Y.Y y;} }
typie paramA
jestX<X<X<A,B>.Y, X<A,B>.Y>.Y, X<X<A,B>.Y, X<A,B>.Y>.Y>.Y
iB
jestX<X<X<A,B>.Y, X<A,B>.Y>.Y, X<X<A,B>.Y, X<A,B>.Y>.Y>.Y
.W następstwie tego wzorca, można sobie wyobrazić tylko 1 pracy kompilator musiałby zrobić, by wydedukować, co
A
sięE
sąY.Y.Y.Y.Y.Y.Y.Y.Y
w definicjiclass X<A,B,C,D,E>{class Y:X<Y,Y,Y,Y,Y>{Y.Y.Y.Y.Y.Y.Y.Y.Y y;}}
.1 Możesz to rozgryźć, ale potrzebujesz dużo cierpliwości, a inteligencja ci tutaj nie pomoże.
źródło
Y
s .Python 3, 13-bajtowe źródło, 9 057,900,463 bajtów (8,5GiB) .pyc-file
Edycja : Zmieniłem kod na powyższą wersję po tym, jak zdałem sobie sprawę, że reguły mówią, że wielkość wyjściowa powyżej 4GiB nie ma znaczenia, a kod dla tego jest jeszcze trochę krótszy; Poprzedni kod - i co ważniejsze wyjaśnienie - można znaleźć poniżej.
Python 3, 16 bajtowe źródło, plik .pyc> 32 TB (jeśli masz wystarczającą ilość pamięci, miejsca na dysku i cierpliwości)
Objaśnienie: Python 3 ciągle się składa, a duże wykładziny szybko się potęgują. Format używany w plikach .pyc przechowuje długość reprezentacji liczb całkowitych przy użyciu 4 bajtów, a w rzeczywistości limit wydaje się bardziej podobny
2**31
, więc używając tylko potęgowania do wygenerowania jednej dużej liczby, limit generuje 2 GB. plik pyc ze źródła 8-bajtowego. (19**8
jest nieco nieśmiały8*2**31
, więc1<<19**8
ma reprezentację binarną nieco poniżej 2 GB; mnożenie przez osiem to dlatego, że chcemy bajtów, a nie bitów)Jednak krotki są również niezmienne, a mnożenie krotki jest również stale składane, więc możemy powielić ten blob 2 GB tyle razy, ile chcemy, przynajmniej
2**31
prawdopodobnie.4**7
Dostać się do 32TB został wybrany tylko dlatego, że był to pierwszy wykładnik udało mi się znaleźć, że piłka poprzednią 16TB odpowiedź.Niestety, mając pamięć na swoim komputerze, mogłem to przetestować tylko do mnożnika 2, tj.
(1<<19**8,)*2
, który wygenerował plik 8,5 GB, co mam nadzieję pokazuje, że odpowiedź jest realistyczna (tzn. rozmiar pliku nie jest ograniczony do 2 ** 32 = 4 GB).Poza tym nie mam pojęcia, dlaczego rozmiar pliku, który uzyskałem podczas testowania, wynosił 8,5 GB zamiast 4 GB, czego się spodziewałem, a plik jest na tyle duży, że w tej chwili nie mam ochoty się w niego grzebać.
źródło
(1<<19**8,)*2
? 4 GB wystarczy.1<<18
na moim komputerze (1,5 GB), ale przetestuję go później na Linuksie, gdzie spodziewam się, że będzie działał z pełnymi 8 GB (nie zamierzam wypróbować wersji 32 TB!)python -m py_compile asd.py
do wygenerowania pliku .pyc.„Szablon Haskell” pozwala na generowanie kodu Haskell w czasie kompilacji przy użyciu Haskell, a zatem jest kompletnym wstępnym procesorem.
Oto moja próba, sparametryzowana dowolnym wyrażeniem liczbowym
FOO
:Magia to kod wewnątrz „splice”
$(...)
. Zostanie to wykonane w czasie kompilacji, aby wygenerować Haskell AST, który zostanie wszczepiony w AST programu zamiast połączenia.W tym przypadku tworzymy prosty AST reprezentujący literał
0
, replikujemy teFOO
czasy, aby utworzyć listę, a następnie używamyListE
zLanguage.Haskell.TH
modułu, aby przekształcić tę listę AST w jeden duży AST reprezentujący literał[0, 0, 0, 0, 0, ...]
.Powstały program jest równoważny
main = print [0, 0, 0, ...]
zFOO
powtórzeniami0
.Aby skompilować do ELF:
Waży to 83 bajty (66 dla kodu Haskell i 17 dla
-XTemplateHaskell
argumentu) plus długośćFOO
.Możemy uniknąć argumentu kompilatora i po prostu skompilować
ghc
, ale musimy umieścić{-# LANGUAGE TemplateHaskell#-}
na początku, co powoduje wzrost kodu do 97 bajtów.Oto kilka przykładowych wyrażeń
FOO
i wielkość wynikowego pliku binarnego:Skończyło mi się kompilowanie pamięci RAM
(2^20)
.Możemy również stworzyć nieskończoną listę, używając
repeat
zamiastreplicate FOO
, ale to zapobiega kompilatorowi;)źródło
[...].replicate (2^10)<$>[|0|])
). Nie mam doświadczenia z Haskellem; jakieś wskazówki, jak to skompilować?<$>
funkcja jest szeroko stosowana w Haskell, ale została przeniesiona tylko do „preludium” (zestawu funkcji dostępnych domyślnie) w GHC 7.10. W przypadku wcześniejszych wersji należy dodaćimport Control.Applicative;
po istniejącymimport
oświadczeniu. Właśnie próbowałem z GHC 7.8.4 i działa.C ++, 250 + 26 = 276 bajtów
Jest to funkcja Ackermann zaimplementowana w szablonach. Nie mogę się skompilować
h=a<4,2>::n;
na moim małym (6 GB) komputerze, ale udało mi sięh=a<3,14>
uzyskać plik wyjściowy o wielkości 26 MB. Możesz dostroić stałe, aby przekroczyć granice platformy - wskazówki znajdziesz w linkowanym artykule z Wikipedii.Wymaga
-g
flagi dla GCC (ponieważ wszystkie symbole debugowania faktycznie zajmują dowolne miejsce) i głębia szablonu większa niż domyślna. Moja linia kompilacji zakończyła się jakoInformacje o platformie
źródło
A+B
w każdej klasie, teraz myślę o tym ...ASM, 61 bajtów (źródło 29 bajtów, 32 bajty na flagi), plik wykonywalny 4,294,975,320 bajtów
Połącz z
gcc the_file.s -mcmodel=large -Wl,-fuse-ld=gold
źródło
1<<30
jest wystarczający dla C. Ponieważ jest to asembler, rozmiar jest w bajtach.as
udaje się oddać offld
, aleld
nie z tego . Nawet nie-mcmodel=medium
wydaje się pomóc.gold
linkera:gcc -fuse-ld=gold ...
kompiluje / łączy ... eek! Zakończone w 1:29 (89 sekund) i rozmiarze 1 073 748 000 bajtów.gcc -o g g.s -mcmodel=large -Wl,-fuse-ld=gold
. Końcowe podsumowanie:,4,294,975,320 bytes
z 32 dodatkowymi bajtami dodanymi do długości programu dla-mcmodel=large -Wl,-fuse-ld=gold
. Warto zauważyć, że nagłówek jest niepoprawny; źródło ma 29 bajtów (bez dodanych dodatkowych flag).1<<33
,8,589,942,616
otrzymałem bajtowy plik wykonywalny.Oto moja odpowiedź C z 2005 roku. Wytworzyłby plik binarny 16 TB, gdybyś miał 16 TB pamięci RAM (nie masz).
źródło
Zwykły stary preprocesor C: wejście 214 bajtów, wyjście 5 MB
Zainspirowany moim prawdziwym preprocesorem zawiodłem tutaj .
Eksperymenty pokazują, że każdy poziom
#define
s spowoduje (zgodnie z oczekiwaniami) wydajność około dziesięciokrotnie większą. Ale ponieważ ten przykład skompilował się dłużej niż godzinę, nigdy nie przeszedłem do „G”.źródło
Java, źródło 450 + 22 = 472 bajtów, plik klasy ~ 1GB
B.java (wersja golfowa, ostrzeżenie podczas kompilacji)
B.java (wersja bez golfa)
Kompilacja
Wyjaśnienie
Ta bomba używa procesorów adnotacji. Wymaga 2 przejść kompilacyjnych. Pierwszy przebieg buduje klasę procesora
B
. Podczas drugiego przejścia procesor tworzy nowy plik źródłowyC.java
i kompiluje go do plikuC.class
rozmiaru1,073,141,162
bajtów.Istnieje kilka ograniczeń podczas próby utworzenia dużego pliku klasy:
error: UTF8 representation for string "iiiiiiiiiiiiiiiiiiii..." is too long for the constant pool
.error: too many constants
.class
pliku. Jeśli zwiększę16380
do16390
powyższego kodu, kompilator nigdy nie zwraca..java
Plik ma również limit około 1 GB . Zwiększenie16380
do16400
powyższego kodu powoduje:An exception has occurred in the compiler (1.8.0_66). Please file a bug ...
po którym następujejava.lang.IllegalArgumentException
.źródło
try..finally
(kod w końcu bloku jest duplikowany dla normalnych i wyjątkowych przypadków) i blok inicjalizujący (kod z bloku inicjującego jest dołączany do każdego konstruktora)ä
przezi
i dostosowane numery. Teraz bomba powinna stworzyć klasę 1 GB w dowolnym systemie bez problemów z kodowaniem. Jednak teraz potrzebuje dużo więcej pamięci.C, źródło 26 bajtów, wyjście 2139103367 bajtów, poprawny program
Opracowano przy użyciu:
gcc cbomb.c -o cbomb
(gcc wersja 4.6.3, Ubuntu 12.04, ~ 77 sekund)Pomyślałem, że spróbuję zobaczyć, jak duży mogę zrobić prawidłowy program bez użycia opcji wiersza poleceń. Pomysł mam z tej odpowiedzi: https://codegolf.stackexchange.com/a/69193/44946 przez Digital Trauma. Zobacz komentarze tam, dlaczego to się kompiluje.
Jak to działa:
const
Usuwa flagę zapisu ze stron w segmencie, dzięki czemu main można wykonać. Jest195
to kod maszynowy Intel do zwrotu. A ponieważ architektura Intela ma charakter endian, jest to pierwszy bajt. Program zakończy działanie bez względu na kod startowy umieszczony w rejestrze eax, prawdopodobnie 0.To tylko około 2 gig, ponieważ linker używa 32-bitowych podpisanych wartości dla przesunięć. Jest o 8 megapikseli mniejszy niż 2 gig, ponieważ kompilator / linker potrzebuje trochę miejsca do pracy i jest to największy, jaki udało mi się uzyskać bez błędów linkera - ymmv.
źródło
Boo , 71 bajtów. Czas kompilacji: 9 minut. 134.222.236 bajtów wykonywalny
Używa makra
R
(dla Powtarzania), aby kompilator pomnożył instrukcję przyrostu dowolną liczbę razy. Nie są wymagane żadne specjalne flagi kompilatora; po prostu zapisz plik jakobomb.boo
i uruchom kompilator,booc bomb.boo
aby go skompilować.źródło
2**e
-co to jest? Spróbuj9**e
!Kotlin , źródło 90 bajtów, 177416 bajtów (173 KB) skompilowany plik binarny JVM
Technicznie możesz to wydłużyć jeszcze bardziej, zagnieżdżając wyrażenie. Jednak kompilator ulega awarii z
StackOverflow
błędem, jeśli zwiększysz rekurencję.źródło
C ++, 214 bajtów (nie są wymagane specjalne opcje kompilacji)
Jest to dość prosta dwuwymiarowa rekurencja szablonu (głębokość rekurencji jest równa pierwiastkowi kwadratowemu całkowitej liczby wysłanych szablonów, więc nie przekroczy limitów platformy), z małą ilością statycznych danych w każdym z nich.
Wygenerowany plik obiektowy
g++ 4.9.3 x86_64-pc-cygwin
ma 2567355421 bajtów (2.4GiB).Zwiększenie wartości początkowej powyżej 80 przerywa asembler gcc cygwin (zbyt wiele segmentów).
Ponadto
99999
można go zastąpić9<<19
lub w podobny sposób, aby zwiększyć rozmiar bez zmiany kodu źródłowego ... ale nie sądzę, że potrzebuję więcej miejsca na dysku niż już jestem;)źródło
-c
flagi kompilacji, aby zatrzymać linker (2 dodatkowe bajty), i nie jestem pewien, czy mogę zaakceptować wyjście .o (nie jeden z tych, które wymieniłem). Wciąż mi się podoba, więc +1.Scala - źródło 70 bajtów, wynik 22980842 bajtów (po słoiku)
Daje to 9 5 (około 59 000) wyspecjalizowanych plików klas, które są pakowane w słoik o wielkości około 23 MB. Zasadniczo możesz kontynuować, jeśli masz system plików, który może obsłużyć tyle plików i wystarczającą ilość pamięci.
(Jeśli polecenie jar musi być dołączone, ma 82 bajty).
źródło
error: java.lang.OutOfMemoryError: GC overhead limit exceeded
. Czy możesz również udokumentować wymagane polecenie do kompilacji?scalac -J-Xmx12G X.scala
To, czego użyłem. Nie sprawdziłem, ile tak naprawdę potrzebuje.error: error while loading AnnotatedElement, class file '/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar(java/lang/reflect/AnnotatedElement.class)' is broken (bad constant pool tag 18 at byte 76) one error found
Czy możesz podać wersję scala i java (być może także platformę)? Użyłem skalaca 2.9.2 i OpenJDK 1.8.0_66-internal-b17 na Debianie 8 x86-64.java version "1.8.0_72-ea" Java(TM) SE Runtime Environment (build 1.8.0_72-ea-b05) Java HotSpot(TM) 64-Bit Server VM (build 25.72-b05, mixed mode)
,$ scala -version Scala code runner version 2.11.7 -- Copyright 2002-2013, LAMP/EPFL
C 284 + 2 bajty dla
-c
INgcc bomb.c -o bomb.o -c
; wyjście: 2 147 484 052 bajtówźródło
Buczenie, o wiele więcej, niż można się po tym spodziewać
źródło
Python 3:
Bomba tetracyjna
źródło