Sem título

Coelho que encontrei por aí

Gostei desse coelho, quero deixar ele famoso.

Aventuras com o Emacs: recuperando arquivos

Dia 6 de agosto, em algum momento próximo a 20:00, o meu editor de textos está aberto. Há rascunhos de ideias que eu havia anotado ao longo do dia. Uso o ambiente/shell Enlightenment para prover alguma interface gráfica para o meu sistema Linux, ambiente que já falhou várias vezes comigo. Não foi diferente dessa vez, o Enlightenment parou, não estava me servindo mais. Mas eu quero meus arquivos de volta, assim começa a história de como perdi algumas horas de minha vida. “Eu quero” é o que basta para fazer isso acontecer.

Primeiro passo, eu utilizo a interface não-gráfica do Linux, um tty diferente. Eu não posso usar a interface gráfica agora, logo vou ter que me acostumar a usar muitos aplicativos simultaneamente (usar o navegador web via TUI enquanto leio o manual de como usá-lo no lado direito, etc). Eu conhecia o tmux, mas não estava habituado a usá-lo, e isso mudou. Passei algumas horas me acostumando a usar o tmux enquanto usava os outros aplicativos. Usei o gdb para interromper o fluxo normal de execução do Enlightenment aberto e chamar a função ecore_app_restart, dica que havia aprendido na lista de email do Enlightenment.

photo554335250209155207

Eu estava perdendo o preconceito de controlar o posicionamento das “janelas” utilizando apenas o teclado. Entretanto, a dica do gdb para reiniciar o Enlightenment, que já me ajudou no passado algumas vezes, dessa vez não teve efeito.

photo554335250209155208

Já são 2:00 no dia seguinte e eu ainda estou pesquisando, na internet, algo que pudesse me ajudar. O Emacs estava aberto e na memória RAM do computador eu poderia recuperar o que eu havia digitado. Foi então que, lendo um arquivo que dava instruções de como depurar o Emacs, eu percebo a informação que vai me ajudar.

Mais uns vinte minutos e eu já tinha salvo o estado de execução no qual o Emacs se encontrava, o core dump file. Hora de voltar a ser produtivo, voltarei a esse problema outro dia.

20 de agosto

O dia que eu retorno ao problema. Passei dias demais sem atualizar meu sistema com receio de distanciá-lo demais do ambiente no qual o Emacs estava funcionando. Hora de ler novamente as instruções do arquivo DEBUG que encontrei no repositório do Emacs. Sigo as instruções e não funciona, meu binário não possui os símbolos de depuração. Considerando a possibilidade de que não fosse funcionar, re-gero o binário do Emacs de forma que os símbolos de depuração fossem salvos no sistema de arquivos.

Hora da verdade. Tento novamente e vejo o esperado warning:

warning: core file may not match specified executable file.

Prossigo de qualquer forma, e, para minha satisfação, recupero as informações que eu queria.

shot-2016-08-20_06-44-01

Trabalho feito, hora de atualizar o sistema. Acho que se fosse no Windows, eu não teria nenhuma chance. De qualquer forma, acho que está na hora de encontrar um substituto para o Enlightenment.

Escolher se aprisionar é uma escolha?

Existe essa discussão (filosófica?) em comunidades de software livre sobre o caso de você escolher abrir mão de liberdade ser uma escolha ou não, e, assim, tentar ridicularizar todo o argumento de liberdade por trás desse movimento, pois, segundo essa lógica, todo software era livre. Eu nunca me interessei por tais discussões, pois quando eram feitas por trolls, eu não me irritava, nem perdia tempo tentando responder. Quando eram feitas genuinamente pela busca de um exercício de filosofia, nenhum argumento que despertasse o meu interesse era apresentado, e eu também não me envolvia em tais discussões. Recentemente, ao acaso, acabei pensando em algo que me fez enxergar o problema de outra forma e ter interesse na resposta (ou no debate).

A forma como eu achei uma forma de tornar essa discussão mais interessante foi misturando uma outra discussão que por si só já é bem grande, a discussão de identidade. Quando você sofre uma experiência, você muda. A questão é se essa mudança que aconteceu é grande o suficiente para considerar que você é uma pessoa diferente. A forma como eu relaciono esses dois debates são através da possibilidade de mudar de ideia.

Um exemplo para tal relação foi a situação que ocorreu com Frank Sinatra, que, segundo o podcast que escutei, fez um acordo com um cara para fazer parte da banda dele, e, segundo as regras do contrato, ele não poderia abandonar a banda, e, se o fizesse, deveria pagar 33% de todo o dinheiro que ele ganhasse durante o resto de sua vida como multa. Ele acabou mudando de ideia e largou a banda. A questão é, esse Frank Sinatra que assinou o contrato era o mesmo que saiu da banda? Se eles são pessoas diferentes, então o Frank do futuro teve sua liberdade reduzida por uma decisão do Frank do passado.

O exemplo anterior pode ter parecido um pouco absurdo, mas é bem válido que instituições criminosas usem de ameaças para lhe forçar a assinar esse tipo de contrato. E se você não pode anular o contrato provando (isso quando existe a possibilidade de você conseguir provar) que você só o assinou por conta de uma ameaça, você efetivamente teve sua liberdade reduzida e “escolher se aprisionar” nunca foi uma escolha.

Para desvirtuar a discussão ainda mais tentando fazer relação com temas distantes, você pode fazer analogia do comportamento que acabei de descrever com a sociedade que você ajuda a construir e entrega para seus filhos. Se eles nascem em um lugar onde a liberdade foi tirada, então quer dizer que em momento nenhum houve uma perda de liberdade, pois na verdade seus pais que “escolheram” abrir mão da liberdade?

Bom, é isso, não conclui nada, mas não seria assim que discussões filosóficas acontecem, onde a discussão é mais importante que a resposta?

Boost.Http has a new parser

I usually don’t blog about updates on the Boost.Http project because I want as much info as possible in code and documentation (or even git history), not here. However, I got a stimulus to change this habit. A new parser I’ve been writing replaced the NodeJS parser in Boost.Http and here is the most appropriate place to inform about the change. This will be useful info also if you’re interested in using NodeJS parser, any HTTP parser or even designing a parser with a stable API unrelated to HTTP.

EDIT (2016/08/07): I tried to clarify the text. Now I try to make it clear whether I’m refering to the new parser (the new parser I wrote) or the old parser (NodeJS parser) everywhere in the text. I’ll also refer to Boost.Http with new parser as new Boost.Http and Boost.Http with old parser as old Boost.Http.

What’s wrong with NodeJS parser?

I started developing a new parser because several users wanted a header-only library and the parser was the main barrier for that in my library. I took the opportunity to also provide a better interface which isn’t limited to C language (inconvenient and lots of unsafe type casts) and uses a style that doesn’t own the control flow (easier to deal with HTTP upgrade and doesn’t require lots of jump’n’back among callbacks).

NodeJS parser argues you can pause it at any time, but its API doesn’t seem that reliable. You need to resort to ugly hacks[1][2] if you want to properly support HTTP pipelining with an algorithm that doesn’t “go back”. If you decide to not stop the parser, you need to store all intermediate results while NodeJS parser refuses to give control back to you, which forces you to allocate (even if NodeJS parser don’t).

NodeJS parser is hard to use. Not only the callback model forces me to go back and forth here and there, it’ll also force me to resort to ugly hacks full of unsafe casts which also increase object size to provide a generic templated interface[1][2][3][4]. Did I mention that it’ll always consume current data and I need to keep appending data everywhere (this is good and bad, the new parser I implemented does a good job at that)? The logic to handle field names is even more complex because this append stuff[1][2][3]. It’s even more complex because NodeJS won’t always decode the tokens (matching and decoding are separate steps) and you need to decode them yourself (and you need to know a lot of HTTP details).

The old parser is so hard to use that I wouldn’t dare to use the same tricks I’ve used in the new Boost.Http to avoid allocations on the old Boost.Http. So the NodeJS parser doesn’t allocate, but dealing with it (old Boost.Http) is so hard that you don’t want to reuse the buffer to keep incomplete tokens at all (forcing allocation or a big-enough secondary buffer to hold them in old Boost.Http).

HTTP upgrade is also very tricky and the lack of documentation for the NodeJS parser is depressing. So I only trust my own code as an usage reference for NodeJS parser.

However, I’ve hid all this complexity from my users. My users wanted a different parser because they wanted a header-only library. I personally only wanted to change the parser because the NodeJS parser only accepts a limited set of HTTP methods and  it was tricky to properly not perform any allocation. The new parser even makes it easier to reject an HTTP element before decoding it (e.g. a URL too long will exhaust the buffer and then the new Boost.Http can just check the `expected_token` function to know it should reply with 414 status code instead concatenating a lot of URL pieces until it detect the limit was reached).

If you aren’t familiar enough with HTTP details, you cannot assume the NodeJS parser will abstract HTTP framing. Your code will get the wrong result and it’ll go silent for a long time before you know it.

The new parser

EDIT(2016/08/09): The new parser is almost ready. It can be used to parse request messages (it’ll be able to parse response messages soon). It’s written in C++03. It’s header-only. It only depends on boost::string_ref, boost::asio::const_buffer and a few others that I may be missing from memory right now. The new parser doesn’t allocate data and returns control to the user as soon as one token is ready or an error is reached. You can mutate the buffer while the parser maintains a reference to it. And the parser will decode the tokens, so you do not need ugly hacks as NodeJS parser requires (removing OWS from the end of header field values).

I want to tell you that the new parser was NOT designed to Boost.Http needs. I wanted to make a general parser and the design started. Then I wanted to replace NodeJS parser within Boost.Http and parts have fit nicely. The only part that didn’t fit perfectly at the time to integrate pieces was a missing end_of_body token that was easy to add in the new parser code. This was the only time that I, as the author of Boost.Http and as a user of the new parser, used my power, as the author of the parser itself, to push my needs on everybody else. And this token was a nice addition anyway (using NodeJS API you’d use http_body_is_final).

My mentor Bjorn Reese had the vision to use an incremental parser much earlier than me. I’ve only been convinced to the power of incremental parsers when I’ve saw a CommonMark parser implemented in Rust. It convinced me immediately. It was very effective. Then I’ve borrowed several conventions on how to represent tokens in C++ from a Bjorn’s experiment.

There is also the “parser combinators” part of this project (still not ready) that I’ve only understood once I’ve watched a talk from Scott Wlaschin. Initially I was having a lot of trouble because I wanted stateful miniparsers to avoid “reparsing” certain parts, but you rarely read 1-sized chunks and I was only complicating things. The combinators part is tricky to deliver, because the next expected token will depend on the value (semantic, not syntax) of current token and this is hard to represent using expressions like Boost.Spirit abstractions. Therefore, I’m only going to deliver the mini-parsers, not the combinators. Feel free to give me feedback/ideas if you want to.

Needless to say the new parser should have the same great features from NodeJS parser like no allocations or syscals behind the scenes. But it was actually easier to avoid and decrease allocations on Boost.Http thanks to the parser’s design of not forcing the user to accumulate values on separate buffers and making offsets easy to obtain.

I probably could achieve the same effect of decreased buffers in Boost.Http with NodeJS parser, but it was quite hard to work with NodeJS parser (read section above). And you should know that the old Boost.Http related to the parser was almost 3 times bigger (it’d be almost 4 times bigger, but I had to add code to detect keep alive property because the new parser only care about message framing) than the new Boost.Http code related to the parser.

On the topic of performance, the new Boost.Http tests consume 7% more time to finish (using a CMake Release build with GCC under my machine). I haven’t spent time trying to improve performance and I think I’ll only try to improve memory usage anyway (the size of the parser structure).

A drawback (is it?) is that the new parser only cares about structuring the HTTP stream. It doesn’t care about connection state (exception: receiving http 1.0 response body/connection close event). Therefore, you need to implement the keep-alive yourself (which the Boost.Http higher-level layers do).

I want to emphasize that the authors of the NodeJS parser have done a wonderful job with what they had in hands: C!

Migrating code to use the new parser

First, I haven’t added the code to parse the status line yet, so the parser is limited to HTTP requests. It shouldn’t take long (a few weeks until I finish this and several other tasks).

When you’re ready to upgrade, use the history of the Boost.Http project (files include/boost/http/socket-inl.hpp and include/boost/http/socket.hpp) as a guide. If you’ve been using NodeJS parser improperly, it’s very likely your code didn’t have as much lines as Boost.Http had. And your code probably isn’t as templated as Boost.Http anyway, so it’s very likely you didn’t need as much tricks with http_parser_settings as Boost.Http needed.

Tufão project has been using NodeJS parser improperly for ages and it’d be hard to fix that. Therefore, I’ll replace “Tufão’s parser” with this new shiny one in the next Tufão release Tufão 1.4.0 has been refactored to use this new parser. It’ll finally gain It finally received support for HTTP pipelining and plenty of bugfixes that nobody noticed will land landed. Unfortunately I got the semantics for HTTP upgrade within Tufão wrong and it kind of has “forced HTTP upgrade” (this is something I got right in Boost.Http thanks to RFC7230 clarification).

Next steps

I may have convinced you to prefer Boost.Http parser over NodeJS parser when it comes to C++ projects. However, I hope to land a few improvements before calling it ready.

API/design-wise I hope to finish miniparsers for individual HTTP ABNF rules.

Test wise I can already tell you more than 80% of all the code written for this parser are tests (like 4 lines of test for each 1 line of implementation). However I haven’t run the tests in combination with sanitizers (yet!) and there a few more areas where tests can be improved (include coverage, allocate buffer chunks separately so sanitizers can detect invalid access attempts, fuzzers…) and I’ll work on them as well.

I can add some code to deduce the optimum size for indexes and return a parser with a little less overhead memory-wise.

EDIT (2016/08/11)

I’ve added a link that should be here since the first version of this post: https://gist.github.com/vinipsmaker/4998ccfacb971a0dc1bd

It adds a lot of background on the project. This is the proposal I’ve sent to get some funding to work on the project.

Tenha um ponto a apresentar, não seja um macaco

Na computação não é incomum o desprezo a coisas primitivas. Primitivos, primatas, macacos… Assim, uma forma de ofender, no meio da computação, é usar a palavra macaco. E quase nunca é uma ofensa de verdade, apenas uma brincadeira. No YouTube, por exemplo, quando o site falha, uma mensagem de “uma equipe de macacos treinados está cuidando do problema” é apresentada. Pois bem, parece que está começando a ganhar popularidade esse jeito de “argumentar” onde você imita um macaco, faz as pessoas rirem, e ganha o debate. Eu tenho uma mensagem que eu gostaria de deixar por aqui.

A mensagem que eu quero deixar é que se você tentar conversar comigo usando essa forma de comunicação de macaco, eu vou ficar irritado. Se seu objetivo for me irritar, parabéns, você conseguirá. E o motivo que me irrita é que eu não quero interagir com macacos, pois eu não tenho empatia nenhuma com estupidez. Agora, o resto desse pequeno texto é dedicado a explicar o que seria “se comunicar como macaco”.

Eu vi recentemente esse vídeo do Maurício Saldanha e fiquei surpreso com a forma de comunicação primitiva dele. Eu fiquei profundamente intrigado, me perguntando se ele achava que estava sendo legal, ou se ele realmente tinha a limitação de se “comunicar” daquela forma. Vejam por vocês mesmos:

Agora, preste atenção no instante 14:47, que merda é essa?! Você pode me explicar? Eu acho difícil de acreditar que um macaco desse está ficando popular. E a razão é que eu fico frustrado. Estou publicamente admitindo que fico frustrado (caso você queira chamar de “recalque”). E o motivo da frustração é que enfrento situações onde as pessoas destacam minha baixa capacidade comunicativa, mas o motivo de eu sofrer dessa baixa capacidade comunicativa é que, (1) ou eu não tenho nada a falar (ou sequer sei o quê falar), (2) ou eu estou tentando comunicar uma ideia mais complexa, nem que seja complexa somente para mim (diferente desse macaco primitivo do Maurício Saldanha).

Olhem de novo esse vídeo! Que merda se passa na cabeça desse macaco? Eu fico puto com esse comportamento ser realizado por alguém da mesma espécie que eu, e que, para tornar as coisas melhores, faz parte da mesma cultura na qual estou inserido (a cultura brasileira). Isso me deixa profundamente frustrado. Se você estiver tentando entender o quanto eu fico irritado (ou “cheio de recalque”, a depender do ponto de vista), imagine o Felipe Neto encenando aqueles vídeos dele lá.

Agora, atuação, e não essa merda incompreensível do Maurício Saldanha, é uma arte, é belo, é algo a se apreciar. Recentemente me deparei com um vídeo do Jim Carrey e grande parte da mensagem que ele estava comunicando, uma parte crucial para o efeito que ela tinha, eram esses “gestos de macacos”. O vídeo era o Unnatural Act:

Agora, a diferença que torna o Maurício Saldanha um babaca estúpido macaco primitivo incompreensível (note como a internet permeia o ódio e me faz xingar um estranho aleatório que nem conheço) e o Jim Carrey um gênio ao fazer “a mesma coisa” é bem simples, então guarde bem com você: o Jim Carrey tem um ponto, não depende só de expressões baratas inseridas aleatoriamente para tentar ridicularizar o ser de opinião contrária.

E para terminar, quero deixar uma dica aqui. Se você estiver precisando de um refrão para uma música punk, usa o refrão “macacos! primatas! primitivos!”. Eu vou ficar contente só com uma menção da ideia original e tal. E também vou escutar a música toda vez que precisar amenizar o ódio que essa comunicação de macaco a qual sou exposto causa em mim.

Lua: a linguagem de script ideal

Outro dia eu assisti a palestra do Andy Wingo sobre Guile Scheme e em um dos slides, uma definição é utilizada para provocar o espectador, “a sloppy language with a slow implementation”. Acho uma forma interessante de começar o texto, pois nesse texto eu vou apresentar o porquê de eu achar lua a linguagem de script ideal.

Aqui, uso o termo script como uma linguagem cujo objetivo é complementar a linguagem principal do seu projeto. Motivos para usar uma linguagem de script seriam uma carga mental necessária menor para manter partes não críticas do projeto.

Simplicidade é importante. Eu estive trabalhando em 3 projetos de problemas/domínios diferentes escritos em soluções/linguagens diferentes (ou diferentes idiomas/sotaques dentro da mesma linguagem) nesses últimos meses e eu posso afirmar que a carga associada a troca de contexto mental que ocorre diariamente não é nada agradável, produtiva, ou benéfica da alguma forma. Você pode confiar no meu julgamento (se o texto fosse em inglês eu iria preferir a frase “believe me”). Alternar entre duas (!) linguagens complexas durante o desenvolvimento do projeto é… overkill.

No começo, achei que não ia querer usar Lua, por ser uma linguagem muito simples (tipo C) e que não respeita várias “boas ideias de programação” (e.g. reaproveitamento de código, abstração, expressividade etc). Mas acontece que mudei minha forma de pensar para focar mais em “isso é um jogo, lua vai ser usado só para scriptar fases, inimigos, etc”. Toda vez que a gente fosse manter o código de script, o que aconteceria seria atenção aumentada somente a uma parte do código (e.g. esse boss não está agindo como imaginei, o que será que aconteceu? vou olhar o código somente desse script e mais nenhum outro código!) e nada de pegar “conhecimento/abstrações” dos códigos antigos. Na verdade, criar padrões ou coisas do tipo poderia até atrapalhar, já que aumentaria a carga necessária para revisar um script que queremos ver independente dos outros. Essa lógica não é bem muito certa, mas penso nela ao me imaginar desenvolvendo o jogo por um longo prazo.

Lua é uma linguagem simples o suficiente para ter conseguido espaço dentro de um kernel “estabelecido”, o NetBSD. E não é incomum ver preocupações de segurança com coisas complexas e comportamentos implícitos, principalmente quando vindo de pessoas que vem de C ou de desenvolvimento de kernel. Mesmo assim, lua conseguiu um pouco de espaço nessa área frente a outras linguagens bem mais antigas e estabelecidas (como Python).

Lua é simples o suficiente para até ter sido escolhida como “linguagem de consulta” para a interface/programa Wireshark.

Lua também possui implementações bem rápidas.

E é isso, lua é tipo Go, medíocre por não tentar fazer nada, mas pelo menos não acaba errando demais e se torna bem simples. E pelos motivos expostos, considero lua a linguagem de script ideal dentre as que conheço.

Eu não confio no seu julgamento se você não critica sua linguagem de programação

A lógica por trás de tal filosofia é bem simples. Se sua linguagem de programação não possui defeitos, então não há nada para mudar nela, pois ela é perfeita, exatamente como está, imutável, nada precisa mudar. Entretanto, e essa é a parte da argumentação que é menos baseada em consequências lógicas e mais baseada em observações ao meu redor, ainda está para ocorrer o momento em que eu veja um entusiasta de sua linguagem de programação que considere negativo o lançamento de uma nova versão de sua linguagem de programação. Cada uma das mudanças que culminou no lançamento de uma nova versão de sua linguagem de programação era uma carência ou um defeito que existia em sua antiga versão, e os “fãs” só irão reconhecê-lo uma vez que o defeito é corrigido, pois sua linguagem é perfeita, sagrada, livre de questionamentos, um tabu quando se menciona a ideia de defeitos.

Pois bem, eu não acho essa posição de sua parte nenhum pouco honesta, e eu quis fazer esse texto para tentar fazer você refletir um pouco a respeito. Outro motivo é que eu estava há muito tempo sem escrever e esse foi um texto fácil para mim, que fluiu da minha mente para “o papel” encontrando nenhuma barreira ou barreiras imperceptíveis. Eu perdi muito pouco tempo para fazê-lo. É um texto de mera opinião.

A ideia de fazer esse texto me veio após perder bastante tempo para escrever a pauta para um podcast que me convidaram a gravar. O tema do podcast seria a linguagem Rust, e eu, na minha mentalidade de blogueiro que ainda não sabe montar pautas, dediquei 4 páginas da pauta só para elaborar o quão C++ é uma linguagem ruim. Agora você precisa entender que a linguagem C++ foi minha linguagem favorita por 6 anos de tal forma que simplesmente não havia espaço para carinho a outras linguagens de programação, e que eu dediquei muito tempo de minha vida só para entender como eu poderia defender essa linguagem. Hoje em dia, C++ não perdeu meu carinho e ela ainda é minha solução favorita para metade dos problemas que resolvo. Ainda assim, 90% do tempo que dediquei para montar a pauta, foi para criticar C++, e em momento nenhum deixei espaço para que o consumidor daquela pauta/obra pudesse imaginar que eu tenho um apego tão grande por essa linguagem.

Acho que uma boa forma de terminar esse texto é ressaltar que sua linguagem só evolui se você corrigir seus problemas, e isso só vai acontecer uma vez que seus problemas sejam reconhecidos. A “linguagem perfeita” é um termo que só é usado por programadores imaturos, grupo do qual um dia eu também já fiz parte.

Impressões de um ebook reader

Resolvi fazer um texto com minhas impressões após ter testado um ebook reader. Resolvi fazer isso após ter tido uma experiência bem positiva com o mesmo, que foi inesperado para mim.

Qualquer ebook reader

Primeiro, uma clarificação. Um tablet é um tablet, e não um dispositivo especializado em leitura de livros que possui uma tela de e-ink otimizada para (1) evitar agressão a sua visão e (2) estender a duração da bateria a níveis espantosos. Logo, nada que se aplique a essas impressões se aplicaria a um tablet.

Segundo, eu achei que eu não gostava de ler, mas ebooks realmente são superiores a livros:

  • Muito leve, é possível segurar com uma única mão não importa em que posição você esteja.
  • Ocupa pouco espaço e carrega todos os seus livros, então você não precisa ter preguiça de ir até a estante para escolher o próximo livro para ler. Pode deixar sempre perto de você e tudo certo.
  • A página não dobra ou “cai” enquanto você está lendo. É sempre confortável.
  • Quando você está lendo aqueles livros mais densos, o livro não precisa usar uma fonte minúscula para economizar espaço. O tamanho da fonte é sempre confortável, pois é você que escolhe.
  • Marca a página para cada livro automaticamente. Nada de preocupação de colocar um marca página antes de largar o livro de lado enquanto se distrai com alguma outra tarefa como atender o celular por 5 minutos.
  • Dicionário. Nunca em minha vida eu fui uma pessoa que costumava ler com um dicionário do lado. Entretanto, acabei descobrindo essa função realmente útil. E melhor de tudo, você não precisa carregar o dicionário junto com seu livro para onde for. E algo tão bom quanto, não precisa sair gastando tempo procurando a palavra no dicionário.
  • Em uma semana eu terminei de ler a metade restante do livro que eu havia interrompido ano passado.
  • Há também uma vantagem que não sei se é muito bem explorada ainda. Assim como no Steam, atualizações são baratas. Assim sendo, toda essa bagunça de procurar por erratas podia diminuir.
  • Você não precisa pagar para ter livros clássicos da literatura cujo direitos autorais já expiraram. Só se você quiser pagar pela tradução de alguém.

Tentei ler HQs no ebook reader também, mas não achei confortável e para alguns desenhos, é melhor ter a experiência de perceber todas as cores e detalhes que o artista fez.

Dicas

Caso esteja procurando o melhor ebook reader e não esteja preocupado com o preço, a lista de funcionalidades que usaria para classificar um ebook reader como sendo versão de luxo seria:

  • Tela de alta resolução.
  • A tela não é minúscula como algumas versões mais baratas. 6 polegadas é um tamanho ótimo.
  • Touchscreen capacitiva.
  • Frontlight.

EDIT (2016/07/14):

E eu completei vários dias estando de posse de um ebook reader baseado em tela de e-ink. Não há muito mais há acrescentar, então nem vou criar um post novo. Só há dois novos comentários que quero fazer.

Dá para ler HQ/comic/mangá tranquilamente também, caso esteja no formato ePUB:

Foto do PocketBook Touch Lux 3 sendo usado para ler a comic 18 days

A câmera do celular não é muito boa, mas garanto que além de legível, a arte continua incrível

O segundo comentário que eu tenho a adicionar, é que você pode escolher uma tela de screensaver legal para funcionar como porta-retrato. Fotos de natureza (ex.: cachoeiras) trazem tranquilidade a algumas pessoas. Aí no meu ebook reader, eu uso como screensaver uma foto que trás tranquilidade para mim (uma brincadeira legal seria você tentar adivinhar o que é comentando por aqui).