Dzieciak uczy się od miesiąca (!!!) języka niszowego, przeznaczonego do wąskiej dziedziny, nie zna żadnego innego języka - pisał podstawy w c++, czyli właściwie nic nie wie, a pisze na forum jak jakiś znawca :D
Gimnazjum, czy liceum?
Dzieciak uczy się od miesiąca (!!!) języka niszowego, przeznaczonego do wąskiej dziedziny, nie zna żadnego innego języka - pisał podstawy w c++, czyli właściwie nic nie wie, a pisze na forum jak jakiś znawca :D
Gimnazjum, czy liceum?
Chciałem mieć pseudo element który będzie pod rodzicem. Wyczytałem sobie
An element with greater stack order is always in front of an element with a lower stack order.
Wytłumaczy mi ktoś dlaczego pierwszy przykład (1 is lower than 2) nie działa, ale drugi (-1 is lower than 2) już tak?
h1.fancy-shadow {
position: relative;
z-index:2;
color: #10BD00;
}
h1.fancy-shadow:before {
position: absolute;
z-index: 1;
font-size: 1.3em;
font-family: "Vivaldi";
opacity: 0.2;
color: black;
content: 'smth';
}
h1.fancy-shadow {
position: relative;
z-index:2;
color: #10BD00;
}
h1.fancy-shadow:before {
position: absolute;
z-index: -1; /* works :| */
font-size: 1.3em;
font-family: "Vivaldi";
opacity: 0.2;
color: black;
content: 'smth';
}
position
to zrozumiesz ;)
W związku z naszym spammeRem przeglądałem dokumentację i znalazłem takie coś:
Co się wydarzyło w tym czerwcu-sierpniu? Lafix odkrył R, że pobrano bibliotekę 25000 razy?
Dzisiaj natrafiłem na pewien problem z rozpakowywaniem paczki szablonu zmiennoargumentowego, który jak dla mnie jest wtf. Szukałem i zastanawiałem się nad przyczyną takiego stanu rzeczy ale nie widzę racjonalnego wytłumaczenia dlaczego nie mogłoby to działać (zostać zaimplementowane). Tyle tytułem wstępu, teraz rzeczony wtf.
Przykładowo mamy, taki kod jak poniżej:
template <typename... Args>
void foo(Args... args)
{
[args...](){};
}
int main()
{
foo(4, 4.5f, "asdasdsa");
return 0;
}
Super wszystko działa. Ale załóżmy teraz, że chcielibyśmy obsługiwać poprawnie r-value i l-value. No to nic prostszego, perfect frowarding i capture by forward panie!
#include <utility>
template <typename... Args>
void foo(Args&&... args)
{
[args = std::forward<Args>(args)...](){};
}
int main()
{
foo(4, 4.5f, "asdasdsa");
return 0;
}
I co?
error: expected ',' or ']' in lambda capture list [args = std::forward<Args>(args)...](){};
Z jakiś przyczyn nie można zachować rozpakowanej paczki w zmiennej.
Workaround to zforwadowanie paczki do tuple:
template <typename... Args>
void foo(Args&&... args)
{
[args = std::std::forward_as_tuple<Args...>(std::forward<Args>(args)...)](){};
}
Btw, trochę w tym temacie, a trochę poza - jeśli byłby ktoś zainteresowany to mogę skrobnąć artykuł na temat tego jak napisać fajny, generyczny singleton w C++ przy użyciu tag dispatchingu, tak by zachować lazy initialization + wymusić initialization, tak gdzie trzeba. Jak by znalazł się ktoś zainteresowany niech da znać via komentarz/PM.
Workaround z GCC do MSVC 6 żeby namespace'y się zgadzały...
typedef ::size_t size_t2;
#define size_t size_t2
winerfresh napisał(a):
W związku z naszym spammeRem
IMHO ostatnio nadużywa się słowa "spam". Przecież nie znaczy ono "flame" lub coś w tym guście (jak to się ostatnio używa), lecz natrętną reklamę.
Och, wait. Zwracam honor.
:P
Przyszła do nas aplikacja po testach penetracyjnych, do poprawy, bo oryginalna firma działająca 7 lat na rynku nie umiała poprawić banalnych SQLi i XSSów wykrytych w czasie audytu. Niektóre identyfikatory zmienione, zamysł kodu ten sam.
To jest diff kodu przed i po "poprawkach bezpieczeństwa". O samej jakości nie wspominam.
#1 - Escaping parametru przed sprawdzeniem, czy w ogóle istnieje - żeby nam ifa nie injectnęli...
- if (empty($_GET['id'])) session_start();
+ if (empty(neutralizeSQL($_GET['id']))) session_start();
#2 - Podmiana bezpiecznego kodu na niebezpieczny
- $result = @mysql_query("SELECT * FROM tbl_firma_kategoria WHERE left_id='".intval($_GET['cat_id'])."'");
+ $result = $db->query("SELECT * FROM tbl_firma_kategoria WHERE left_id=".$_GET['cat_id']." LIMIT 1");
#3 - Cast na inta i escaping
- $result2 = @mysql_query("SELECT * FROM tbl_firma_galeria WHERE user_id=".$firmaDane['user_id']);
+ $result2 = $db->query("SELECT * FROM tbl_firma_galeria WHERE user_id=".neutralizeSQL(intval($firmaDane['user_id'])));
#4 - Zobaczył w dokumentacji operator trójargumentowy i zaczął nim szpanować w losowych miejscach, zamiast zrobić ifa
$redir = (isset($redir)) ? base64_decode($redir) : false;
$redir = (!empty($redir) && check_url(CONFIG_SITE_URL.'/'.$redir)) ? $redir : false;
#5 - SQL injection w wyrażeniu bardzo poważna sprawa
if (intval(neutralizeSQL($_GET['mode'])) == 1) {
// ...
}
#6 - Tylko nie myślcie, że neutralizeSQL to jakaś sensowna funkcja
Neutralizacja HTML do zapytania SQL, szczena mi opadła do samej d**y.
function neutralizeSQL($var, $kind='tekst') {
global $db;
$zmienna = htmlspecialchars(strip_tags($zmienna));
switch ($rodzaj) {
case 'numeric':
return $db->real_escape_string(str_replace(' ', '', str_replace(',', '.', $zmienna)));
break;
case 'text':
return $db->real_escape_string($zmienna);
break;
}
}
(mała poprawka na prośbę autora - msm, i dzek)
#7 - Hashowanie haseł
Oczywiście to substr($x, 0, 2)
rozpieprzy hasha typu $2a$08$
albo $5$rounds=5000$
.
$dbpasswd=$setinfo['usr_password'];
$non_crypt_pass = $usr_password;
$old_crypt_pass = crypt($usr_password,substr($dbpasswd,0,2));
$new_pass = $uzytkownikClass->posol($usr_password);
Gdzie funkcja posol($x)
to:
//posolenie hasła
function posol($haslo) {
#$salt = "4f4d34kjb7574sdoias1190ic3"; //przynajmniej 22 znaki
#return crypt(md5($haslo), '$2a$10$'.$salt);
return md5($haslo);
}
Więc generalnie hasła hashowane czystym MD5 bez soli.
#8 - Register globals by design
extract($_GET, EXTR_OVERWRITE);
extract($_POST, EXTR_OVERWRITE);
extract($_COOKIE, EXTR_OVERWRITE);
Nawet nie sądziłem, że coś takiego w stdlibie PHP w ogóle istnieje i ktoś może tego używać.
W którymś powyższym kodzie parametr $redir
pochodził oczywiście bezpośrednio rozpakowany z $_GET
. Problem zaczyna się, kiedy chcielibyśmy prześledzić skąd się dana zmienna wzięła. Tego czasem nie wiedzą sami developerzy:
if($_COOKIE['foo'] == $foo) {
// ...
}
#9 - Ciastko "uzytkownik"
Autoryzacja polega na wysłaniu użytkownikowi ciastka uzytkownik
w składzie:
id,login,hash_hasla_md5
Audyt oczywiście to wykazał i zalecenie było, żeby użyć losowego tokena, bo dowolny SQL injection umożliwiający na odczyt bazy umożliwia w banalny sposób craftowanie takich ciasteczek. Zalecenie oczywiście od góry do dołu olane.
Próbowaliśmy już to zrefaktoryzować i przenieść ciastko do wewnątrz sesji PHPowej - bo ona również jest startowana. Wykonanie niektórych akcji w systemie powodowało wylogowanie z dość enigmatycznego powodu. Po paru godzinach grzebania okazało się, że niektóre moduły robią np. coś takiego:
$_SESSION = false;
#10 - Uwaga intruz
Jak komuś jeszcze nie wybuchł mózg to dobiję w ostatnim punkcie.
if (empty($_POST['aid']) OR empty($_POST['pwd'])) {
$admintest=0;
$alert = "<html>\n";
$alert .= "<title>UWAGA INTRUZ!!!</title>\n";
$alert .= "<body bgcolor=\"#FFFFFF\" text=\"#000000\">\n\n<br><br><br>\n\n";
$alert .= "<center><img src=\"images/loading.gif\" border=\"0\"><br><br>\n";
$alert .= "<font face=\"Verdana\" size=\"+4\"><b>Prosimy opuścić tą stronę!</b></font></center>\n";
$alert .= "</body>\n";
$alert .= "</html>\n";
die($alert);
}
Lepsze decyzje projektowe podejmowałem w gimnazjum. Przypominam, że firma która to zrobiła działa na rynku od 7 lat i ma się dobrze. Mam to poprawnie połatać. Nie wspomniałem jeszcze, że wejście na stronę główną generuje około 330 zapytań, niektóre lecą z pętli.
Jak nie dam znaku życia, to znaczy że utknąłem w ulubionym pubie. Over.
Przez kod od obsługi koszyka przewija się...
$koszykClass->aktualizyjKoszyk(...);
Pierwszy raz w życiu stwierdzam, że ten kod to po prostu brute force. Jeszcze jeden fajny kawałek:
function zrobCos($arg, $arg2) {
// jakieś operacje na $arg i $arg2
$arg = $_COOKIE['arg']; // totalnie zmieniamy kontekst, lol
unset($arg); // na sam koniec funkcji unsetujemy zmienną - po co, czemu? :O
}
Czy ktoś tam umiał programować? Rozumiał koncepcję funkcji, pętli? To pytanie pozostaje otwarte.
@up to jeszcze nic. Ja przejąłem raz projekt CRM gdzie na podstronie było generowane 1000 zapytań, a na kolejnej prawie 2 tyś. A klient narzekał na słabą wydajność aplikacji :)
Helena, mam zawał.
zipCodeIsBad
;-P
Korzystam w javie z biblioteki która ma obrabiać obrazki. Nie jestem pewien czy tylko ta czy wszędzie tak jest, ale u mnie jeżeli mam czarno biały obraz, i ustawie całemu obrazowi png Transparency: 255
. To mimo że nie ma tam nic widocznego, to i tak taka funkcja (która działa jak różdżka do wybierania kolorów) rozróżnia np rgba(255,255,255,0)
i rgba(0,0,0,0)
.
Wydaje się logiczne, ale jednak przezroczysty to przezroczysty.
PS; Co dziwne, jeżeli wezmę kolor z jednego miejsca ("jasna" przezroczystość) i spróbuję wylać farbę na inne ("ciemna" przezroczystość) to wyjściowy plik jest taki sam (nawet sprawdziłem checksumy md5). Po wczytaniu sytuacja z funkcją-różdżką powtarza się.
Na drugim semestrze informatyki miałem zastępstwo na programowaniu w C z jakimś doktorantem, proste programiki konsolowe, zalecał dawanie samych if
. Zrobiłem operator wyrażenia trójargumentowy:
zmienna = warunek ? wyrazenie1 : wyrazenie2;
Dziwił się, że tak można i się z tym jeszcze nie spotkał, ale skoro działa to chyba może być. Ech :)
`dodanie znaczników i ```
- @furious programming
@up Ja zajęcia z podstaw Javy miałem z takim doktorem który chyba dość dawno nie napisał żadnego programu. Pokazywał nam slajdy z podstawami. Pętle, ify itp, jednocześnie je omawiając. Doszedł do wyrażenia trójargumentowego. Zatrzymał się na tym slajdzie. Czyta opis przez chwilę. W końcu mówi coś w stylu "A no to jest taki operator warunkowy dla zaawansowanych, tego nie będziemy używać"
Inna sytuacja, ten sam doktor. Każdy miał sobie wymyślić program który napisze na zaliczenie półroczne w Javie. Ktoś tam mówi że chciałby napisać klienta e-mail. Tłumaczy doktorowi jak to ma działać, że będzie można z programu odbierać pocztę, wysyłać maile itp. Doktor na to "Wysyłać? Nie bo to .. spam .. FBI .." Tu właśnie nie pamiętam, ale coś gadał o spamie, o FBI, CIA albo NSA że to się może im nie spodobać czy coś w tym stylu.
A z kolei inny wykładowca ciągle na .Net mówił kropka-net.
Ehh jeszcze tylko miesiąc tych studiów.
Ja zajęcia z podstaw Javy miałem [...] W końcu mówi coś w stylu "A no to jest taki operator warunkowy dla zaawansowanych, tego nie będziemy używać"
W sumie coś w tym jest, jednak jeśli to były zajęcia z podstaw programowania, to lepiej najpierw nauczyć się tworzenia zwykłych warunków, zanim zacznie się stosować skrócone wersje.
@up Tylko, że to nie są żadne skrócone wersje, lecz wręcz odwrotnie. Wyłącznie dzięki temu operatorowi można w językach o składni C pisać wyrażenia funkcyjne, no i kod jest zwyczajnie czytelniejszy (ten dobrze napisany, z właściwymi nazwami funkcji/metod/zmiennych).
Naturalnym rozwinięciem tego podejścia do wyrażeń są wyrażenia łańcuchowe, które pozwalają zapisać kod programu w postaci funkcyjnej. Czyli to, co obecnie jest modne... :)
A to, że na polskich uczelniach zostają doktoranci bo są zbyt ciency, żeby pójść do dowolnej firmy, to tylko pokazuje, że na polskich uczelniach niczego nie można się nauczyć. W sumie to korzystne nawet bo płace rosną. :D
if
był wyrażeniem a nie blokiem (vide Lispy, Ruby, Rust, Scala). Operator trójargumentowy w Javie jest raczej problemem niż jego rozwiązaniem (dodali go chyba tylko z sentymentu).
@Olamagato - to są skrócone wersje warunków; Prosty przykład:
if(intValue == 5)
intResult = 4;
else
intResult = 18;
niektórzy lubią też grupować pojedyncze wyrażenia klamrami:
if(intValue == 5)
{
intResult = 4;
}
else
{
intResult = 18;
}
Powyższe można skrócić do czytelnego jednolinijkowca:
intResult = intValue == 5 ? 4 : 18;
więc operator ten służy jak najbardziej do skracania kodu przy tak prostych warunkach; A nawet stwierdziłbym, że przydaje się on tylko i wyłącznie do przypisań, ewentualnie jeszcze do zwracania rezultatu funkcji;
Poza tym można spokojnie pisać kod, znając zwykły if
, ale trójargumentowca nie; W drugą stronę to nie działa - często obsługuje się tylko jeden stan warunku (prawdę lub fałsz), czego operatorem trójargumentowym nie można zastąpić;
Wyłącznie dzięki temu operatorowi można w językach o składni C pisać wyrażenia funkcyjne
Nawet jeśli to prawda, to zauważ, że na zajęciach z podstaw programowania ludzie i tak nie mają pojęcia o tym, o czym się do nich mówi (chyba że ktoś wcześniej programował); Naturalne jest uczenie się stopniowo, od najbardziej podstawowych rzeczy, a zwykły warunek if
jest elementem bardzo podstawowym; A jak już ogarnie się sens używania instrukcji warunkowych, to wtedy będzie można prezentować dodatkowe rzeczy na ich temat, w tym operator trójargumentowy.
czego operatorem trójargumentowym nie można zastąpić;
, można, tyle że jest to brzydkie i bezsensowne.
nie da się
;)
$(element).foo()[zmienna ? 'bar' : 'baz']().abc()
ale to czyste zło ;-)
@up Polecam napisanie za pomocą samych ifów poniższej procedury (z kodu produkcyjnego):
private void moveFocusForward(boolean forward) {
List<Node> nodes = queryPane.getChildren().stream()
.filter(node -> node instanceof TextField).collect(Collectors.toList());
Iterator<Node> it = nodes.iterator();
for(Node previous = null, next; it.hasNext(); previous = next) {
next = it.next();
if(next.isFocused() && (forward ? it.hasNext() : previous != null)) {
(forward ? it.next() : previous).requestFocus();
return;
}
}
}
Dla mnie brak możliwości użycia instrukcji wyrażeniowej, to w wielu wypadkach kompletnie nieczytelny kod (a to co widać, jest już i tak wystarczająco skomplikowane). Moim zdaniem nie da się takiego kodu napisać prościej i czytelniej nie tracąc nic z obecnej funkcjonalności. Ale może ktoś mnie zaskoczy? :)
TextField
a i kod metody, który ma coś robić jest napisany prawie jak książka. No ale niektórzy wolą pisać dziwne wyrażenia, bo mogą się pochwalić skutecznością eliminacji enterów ;-)
Nowy projekt. No prawie nowy, bo klient podsyła nam to co spłodził jego dział IT za pomocą zip+email. Odpalamy Sonara
Sonar wyliczył Technical Debt na 25 dni... 708 issues... w tym np coś takiego:
if (dataSource == null) {
synchronized (connectionJndiName) {
//....
}
}
Może nie do końca programistycznie, ale... zachciało się przez Windows Update nowego .Net 4.5.2, jednak wciąż instalacja poprawki kończyła się błędem. Dziwnym jakimś, bo bez informacji w necie na jego temat. Pobrałem paczkę do instalacji wspomnianego frameworka, niecałe 70 MB. Uruchomiłem i.. już znam przyczynę, dla której był problem z instalacją...
:)
Wiem, że to ani nowe, anie programistyczne, tylko analityczne, ale wtf jest:
Android używa MTP zamiast normalnego przesyłania danych do kompa.
Wszystkie pędraki, stary telefon, a nawet samochód mogę podmontować normalnie, ale już android który jest linuchem wybrał sobie jakiś standard ms.
JavaScript :D
> '5' - 3
2 // weak typing + implicit conversions = headaches
> '5' + 3
'53' // Because we all love consistency
> '5' - '4'
1 // string - string = integer. what?
> '5' + + '5'
'55'
> 'foo' + +'foo'
'fooNaN' // Marvelous.
> '5' + - '2'
'5-2'
> '5' + - + - - + - - + + - + - + - + - - - '-2'
'52' // Apparently it's ok
> var x = 3;
> '5' + x - x
50
> '5' - x + x
5 // Because fuck math
zajumane z reddita
+
od konkatenacji .
.
Może nie programistyczne ale związane z Windowsem.
Ostatnio słyszałem więcej dźwięków z otwierającego się MessageBox'a z Windowsa, myślałem że to wirus. Skanowałem kilka razy, czyściłem rejestr i nic. Okazało się że w pewnej piosence występuje bardzo podobny dźwięk. Ciekawe kto zgadnie co to za piosenka ;)
Dodawanie dat na stringach:
int daysUntilExpriry = czytamy z bazy
string dateAsText = DateTime.Now.AddDays(daysUntilExpriry).ToString("yyyy-MM-dd") + "T23:59:59";
DateTime returnDate = DateTime.ParseExact(dateAsText, "yyyy-MM-ddTHH:mm:ss", null);
Ach, ten angielski profesjonalizm.
Przepisuję właśnie algorytm tokenizera formatowanego labelka, tyle że stosując inne podejście - zamiast kopiować fragmenty łańcucha do tokenów, po prostu zapamiętuję wskaźniki na początki i końce fragmentów sekcji (nagłówków lub paragrafów); Dzięki temu łańcuch kodu labelka będzie tylko jeden, a lista tokenów (czy tam ich macierz) będzie przechowywać pary pointerów;
Wszystko mam gotowe, testowy kod wygląda tak i pod FPC działa wyśmienicie; Ale jak zwykle Delphi7 się buntuje - ostatnia literka ostatniej sekcji to h
, a ten skądś wytrzasnął d
:
Na dodatek do procedury AddSection
trafiają wskaźniki z poprawnymi znakami (sprawdziłem pod debugerem), a druga pętla nie zmienia parametru ALast
- potem już tylko przypisanie i tyle; Dlaczego więc w procedurze ShowSectionsContent
wskaźnik wskazuje na jakąś dziwną literkę? Zielonego pojęcia nie mam; Próbowałem tę ostatnią sekcję modyfikować i modyfikacje działają super, ale ta konkretna sprawia problemy...
Dziwne to, ale ważne że FPC robi to poprawnie.
while pchrBegin < pchrLast do begin while (pchrBegin <= pchrLast)
WTF. poza tym w jednym miejscu Last
traktujesz jako wskaźnik na ostatni znak, a w drugim miejscu pchrLast
jako wskaźnik na miejsce za ostatnim znakiem - niekonsekwentnie. na 99% masz UB które się objawia na D7.
pchrBegin < pchrLast
jest konieczne, to nie WTF; Jeśli o Last
chodzi, to pchrLast
wskazuje zawsze na ostatni znak kontentu, a ALast
lub Last
w macierzy zawsze na ostatni znak sekcji; To nie jest niekonsekwencja, tylko brak wyboru - pól nie mogę nazwać Begin
i End
(z wiadomych przyczyn), dlatego w kodzie testowym nazwałem je po prostu First
i Last
, jako pierwszy i ostatni znak sekcji;
Podczas dodawania AddSection masz
AFirst: 'third paragraaa"q'
ALast: 'a"q'
zmieniłem też tekst na poniższy ale to jest to samo:
SAMPLE_CODE = AnsiString('q
first headerq"first paragraph"q"second paragraph"q
second headerq"third paragraaa"q');
A jednak nie...
musisz zamienić
procedure ExtractSectionsText(ACode: AnsiString);
na:
procedure ExtractSectionsText(var ACode: AnsiString);
Działasz na tymczasowej kopii łańcucha w tej funkcji.
@szopenfx - nawet jeśli bym działał na kopii, to ta kopia będzie dokładnie taka sama jak oryginał;
Ale czaję co źle zrobiłem - trzeba przekazać łańcuch przez referencję i nie będzie problemu... :] Normalnie operuję na polu klasy komponentu, więc parametry mi niepotrzebne, jednak przy przenoszeniu kodu zastąpiłem parametrem odwołanie do pola i tu wałek z kopią;
Tyle że nadal WTFem jest to, że tylko i wyłącznie taka wartość parametru daje niepoprawne wyjście - jeśli zmienię o jeden znak ciąg ze stałej, to wyjście będzie już poprawne; Próbowałem na różnych ciągach i różnych kompilatorach, no i tylko ten konkretny daje nieprawidłowe wyjście (tylko w D7);
Czas chyba w końcu iść spać o normalnej porze...
Copy
, Delete
czy Pos
, które działają dużo wolniej; Gdyby kod był powolny, to rozciąganie komponentu powodowałoby migotanie zawartości;
Krolik napisał(a)
No, jesli Delphi nie umie zoptymalizować dostępu za pośrednictwem indeksów, to faktycznie inaczej pewnie się nie da.
Umie takie rzeczy optymalizować, FPC tak samo (którym kompiluję kod);
Czyli nie jest to w takim razie WTF Twojego kodu, a projektu biblioteki / kompilatora, że wszystko na wskaźnikach wymusza (rodem z C)
Tzn. nie wszystko, a niektóre z używanych przeze mnie funkcji, które operują na łańcuchach kodowanych w UTF-8; Głównie chodzi o funkcję UTF8CharacterToUnicode
, która przyjmuje wskaźnik na pierwszy bajt znaku; Jakoś szczególnie nie utrudnia to przetwarzania łańcuchów, wręcz przeciwnie;
Chodziło mi tylko o to, że wskaźniki są bardzo błędogenne; np. to kompilator nie zagawarantuje Ci, że AFirst i ALast wskazują na część tego samego stringa.
Kompilator gwarantuje mi, że wskaźniki będą wskazywać na istniejące znaki w istniejącym łańcuchu, pod warunkiem że po ustawieniu wskaźników nie zmodyfikuje zawartości łańcucha - ale to normalne i nie dotyczy jedynie wskaźników na znaki; Moim błędem było przekazanie łańcucha przez wartość, dzięki czemu otrzymywałem kopię łańcucha, wskaźniki ustawiałem na znaki w skopiowanym łańcuchu, a po wyjściu z procedury referencja do łańcucha zostawała utracona (normalne zachowanie); Czyli wskaźniki wskazywały na pozostałość po łańcuchu; Gdybym przekazał łańcuch przez referencję, to wszystko by grało;
Do tego bardzo łatwo popełnić właśnie różne dziwne błędy z zamazywaniem pamięci.
Jeśli myśli się podczas pisania kodu (co czasem niestety, ale zawodzi), to nic się nie podzieje; Tym razem podczas przenoszenia kodu z klasy do testowego programu, nie pomyślałem o poprawnym przekazaniu parametru, dlatego też wyszedł wielki WTF; A tu się okazuje, że wszystko przez głupie niedopatrzenie;
Do przetwarzania łańcuchów używam wskaźników, dlatego że poprawny kod wykorzystujący je, pozwala na napisanie bardzo szybkiego skrawka kodu; Oczywiście można posiłkować się funkcjami z biblioteki standardowej, takimi jak Pos, PosEx, Copy czy Delete, jednak wielokrotne ich użycie znacznie spowalnia algorytm, albowiem wykonują kupę zbędnych rzeczy;
Weźmy na przykład pierwszą (wewnętrzną) pętlę z mojego kodu - szuka ona otwierającego znacznika, pomijając inne znaki; Jest to jedna pętla, gdzie w argumentach sprawdza czy wskaźnik na znak jest mniejszy od adresu ostatniego znaku, następnie czy wskaźnik wskazuje na znak znacznika i jeśli nie - inkrementuje wskaźnik za pomocą Inc - krótko i efektywnie; A teraz weźmy pod lupę funkcję PosEx, która ma nam zastąpić naszą pętlę; Oczywiście ona także używa wskaźnika do przeszukiwania łańcucha, jednak wykonuje zbędne sprawdzanie długości łańcucha, traci czas na inicjowanie zmiennych lokalnych oraz wymusza skopiowanie dwóch łańcuchów (w parametrach przekazanych przez stałą), aby móc wykonać przeszukiwanie lokalnie; W rezultacie działa dwa razy dłużej od mojej pętli; Niestety, ale taka jest cena uniwersalizmu;
Niby tak prosta operacja trwa bardzo krótko, jednak wywołań tej funkcji w procesie rozdzielania kontentu na sekcje będzie dziesiątki - a to tylko podział na sekcje; Kolejny algorytm dzieli każdą sekcję na słowa, który de facto także robi to na podstawie własnej pętli i wskaźników; Jeśli w nim znów skorzystać z PosEx - znów tracimy czas; A w jednym labelku słów może być setki, więc ma to duże znaczenie; Niestety dzielenie kontentu na sekcje i słowa to dopiero 10% procesu tokenizowania; Następnie każdą sekcję trzeba podzielić na linie (mierzyć kolejne słowa), według bieżacego stylu fontu, potem wyznaczyć punkty, w których będą zaczynać się linie; W następnej kolejności trzeba przewalić wszystkie tokeny i wydobyć informacje o linkach - pobrać regiony, w których kursor ma się zmienić, a treść linku przemalować;
To chyba tyle - jak widać kupa kodu; Kod musi być efektywny, dlatego że wszystko co opisałem wyżej, wykonywane jest po pierwsze każdorazowo przy zmianie szerokości komponentu (np. przy rozciąganiu okna z zakotwiczoną do obu boków etykietą), a po drugie - kilkakrotnie przy stworzeniu komponentu; Gdyby kod był wolny - rozciąganie komponentu powodowałoby migotanie całości i nędzny efekt;
Tak więc używam wskaźników, aby maksymalnie jak tylko potrafię przyspieszyć kod (oczywiście jeśli jest to konieczne); Większość algorytmów z modułu TSInfoUtils
(z mojej biblioteki do obsługi plików TreeStructInfo
) pisałem używając wskaźników, zamiast instrukcji z RTL; To pozwoliło zoptymalizować kod procedur i funkcji nawet o 50%;
Kiedyś nie przepadałem za wskaźnikami, nawet uznawałem je za zbędne; Jednak jak już je poznałem to polubiłem od razu, dlatego że pozwalają na pisanie szybkiego kodu; A jak piszę jakiś kod, to sam od siebie wymagam, aby wycisnąć z niego jak najwięcej się da :]
if (!LOG.isDebugEnabled()) {
return pjp.proceed();
}
long time = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
time = System.currentTimeMillis() - time;
LOG.debug("{}[args:{}] executed in {}ms", pjp.getSignature(), pjp.getArgs(), time);
}
pomiar czasu... debug...cóż więcej można chcieć... (kod z bratniego wschodu).