Итак, моё мнение (точнее не мнение, а мой опыт.
внимание info21 тут нет квантора всеобщности! (вообще его нигде нет, если это не указано явно) ):
1) Обе реализации очень плохо читаются и воспринмаются, до того как прочтешь документацию по base64 и не попытаешься реализовать что-то своё. Т.е. реально до написания своей реализации оба исходника представлялись одинаково низкоуровневой абракадаброй. Т.о. они не самодкументирующиеся.
2) После чтения док и написания своей реализации, оба исходника воспринимаются и понимаются нормально. Т.е. нет проблем с верификацией там и там.
3) С99 алгоритм сделан более прямолинейно и дубово. Соответственно читается абсолютно линейно. Основное отличие от реализации на КП (и, возможно, достоинство) -- он пишет в выходной буфер ASAP, т.е. как только что-то сложилось, так сразу и написали. Не дожидаясь пока прочтутся все 3 байта. Также не используется дополнительный буфер. Т.о. нет фазы копирования в промежуточный буфер.
4) В С99 варианте на каждую итерацию цикла существенно больше проверок. Насколько это скажется на производительности -- затрудняюсь оценить. Но вообще, это не хорошо.
5) В обоих реализация используется цикл while. В обоих релизациях соответственно где-то в потрохах цикла меняется счетчик цикла. Но это не страшно. В обоих случаях он меняется в одном месте. Страшно другое -- всякие разные указатели и индексы в обоих реализациях, которые, вообще говоря, связаны со счетчиком, скачут как бешаные лошади. Т.е. их изменение тонким слоем размазано по всему телу цикла. Верифицировать когда и в каких случаях индексы/указатели смогут вылезти за границы дозволенного -- сложно. Нужно считать и думать.
6) В С99 явный перебор со скобочками и операторами. К тому же вариант форматирования выбран не самый удачный.
7) Вообще в обоих вариантах всё слишком низкоуровнево. Впрочем я уже говорил
В КП-варианте имеем два непонятных аргумента у функции, который используется вроде как локальные переменные. Предполагается что там будет вначале ноль. Но почему бы их не обнулить явно?
9) Возвращаемые значения всё же лучше передавать в виде результата функции.
10) В КП-вариенте предполагается, что выходной буфер всегда достаточного размера, что, в общем то, не очевидно. И может привести к ошибкам. (да, словленный AV это тоже ошибка, трапы в Обероне -- деталь реализации, равно также как и в С99, так что отмазкой не являются).
11) Про thread safe... Интересно в КП варианте, если в процессе кодирования, кто-то слегка чуть-чуть поиграется с p,q. Ведь они переданы по ссылке.
12) В КП варианте увлекаются множеством операций на одной строке. Плохо сказывается на читабельности.
Резюме -- в качестве референсной реализации rfc-писателям не стоило указывать эту C99 реализацию, КП-реализация в качестве референсной также не подошла бы. Слишком много низкоуровневых деталей.
Что касается моей реализации, наверно лучше не мне судить. Но она мне не слишком нравится.
Во-первых используются C-style hasks (см. как я работаю со switch'ами), это не портабельно на другие языки (не С-семейства в плане синтаксиса), т.о. на референсную реализацию для императивных языков не тянет.
Во-вторых всё ещё много низкоуровневых деталей. Реверансы в сторону быстродействия. Те же switch'и например. Т.о. оно не тянет и на общую референсную реализацию (в которой должно быть кристально ясно что такое base64 и без чтения спек).
В-третьих мне не удалось достичь макимальной производительности. Т.е. по паре лишних сравнений на итерацию всё же есть. Т.о. для сурово-промышленной эксплуатации также возможно не лучший вариант.
Что хорошо: переменные циклов и прочее от них зависящее меняется только в потрохах for'a, т.е. не в теле цикла. Проще читать. Больше уверенности, что не будет нагажено в память. Вроде бы получилось создать достаточно человекочитабельный код в плане работы с масками. Т.е. не нужно вспоминать что такое есть 3F и почему это в данном случае хорошо. Это видно просто визуально. Ну и быстродействие всё же не столь ужасное как могло бы быть.
Резюме -- я пытался достичь сразу трех целей одновременно: компактности кода (по возможности не должны повторяться одни и те же блоки кода), скорости работы алгоритма (желательно чтобы скорость не уступала любым другим реализациям), хорошей человекочитабельности (в коде должен присутствовать некий ритм помогающий его читать). В резельтате от кода несет компромисом.