Akash's Blog

_

Saturday, July 27, 2024

Base64 Encoder

Base64 is popular system that transforms the binary data into limited set of 64 characters.

For example, text abc can be represented in binary as 011000010110001001100011, Base64 divides this binay data into group of 6 bits 011000 010110 001001 100011 and converts it to as per the encoding table.

Base64 Encoding Table has total 64 (2^6) characters.

  • 0 to 25 : A to Z
  • 26 to 51 : a to z
  • 52 to 61 : 0 to 9
  • 62 : Plus symbol ‘+’
  • 63 : Forward slash ‘/’

Additional symbol ‘=’ is used for padding in case of bits are not in multiple of 6.

According to encoding table, 011000 010110 001001 100011 which is 24 (011000) 22 (010110) 9 (001001) 35 (100011) converted to YWJj in Base64 format.

public class Encoder {

    private static final Encoder ENCODER = new Encoder();

    private static final int ENCODE_BASE = 6;

    private final char[] encodingTable = new char[64];

    private Encoder() {
        initEncodingTable();
    }

    public static Encoder getInstance() {
        return ENCODER;
    }

    public String encode(String input) {
        int[] binary = toBinary(input);
        StringBuilder encoded = new StringBuilder();

        for (int start = 0; start < binary.length; start += ENCODE_BASE) {
            int end = Math.min(start + ENCODE_BASE, binary.length - 1);

            int power = ENCODE_BASE - 1;
            int charIndex = 0;
            for (int j = start; j < end; j++) {
                charIndex += (binary[j] * Math.pow(2, power--));
            }

            encoded.append(encodingTable[charIndex]);
        }

        int pad = binary.length % ENCODE_BASE;
        encoded.append("=".repeat(pad));
        return encoded.toString();
    }

    private int[] toBinary(String input) {
        final int bits = 8;
        int[] binary = new int[input.length() * bits];

        try {
            byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
            int index = 0;
            int max = (int) Math.pow(2, (bits - 1));

            for (byte b : bytes) {
                int val = b;
                for (int i = 0; i < bits; i++) {
                    binary[index++] = ((val & max) == 0 ? 0 : 1);
                    val <<= 1;
                }
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        return binary;
    }

    private void initEncodingTable() {
        int index = 0;

        for (char i = 'A'; i <= 'Z'; i++)
            encodingTable[index++] = i;

        for (char i = 'a'; i <= 'z'; i++)
            encodingTable[index++] = i;

        for (char i = '0'; i <= '9'; i++)
            encodingTable[index++] = i;

        encodingTable[62] = '+';
        encodingTable[63] = '/';
    }
}

This program is implemented to make it more easier to understand. However, for optimal approach we can look into java.util.Base64 class which is bit complex to understand and considers much bigger character set and scenarios than the implemented one.

Thursday, July 18, 2024

5 mistakes I made as a software developer



Undoubtedly, mistakes are part of everyone's life. The challenge is to learn from the mistakes and avoid repeating them in future. 

As a programmer, you will gradually improve in career by contributing to different projects, coding contests, pair programming etc. But the major aspect of improvement lies in learning from the mistakes.

Today I will talk about 5 mistakes that I did as Software Developer that I could have avoided.

1. Try to focus more on solution rather than problem

This mistake is not only applies to coding but in general to any problem of our lives. We are so desperately invested in the solution that we forget about the problem or we do not pay enough attention to the problem.

I learned that the more we focus on problem the better solution we can deliver, that will not just give good value to the customer but also a deep and clear insight into the solution as well.

2. Deliver things without enough Test Cases

I have underrated the value of unit testing and especially TDD for a long time. The confidence you get on your work comes from enough and solid test cases, is imppacable. 

The value you build is not just immediate for your confirmation or avoiding issues in your delivery but this is for all those developers writing the code which may impact your changes and can confidently make changes in the same code because of efforts you put in for writing required test cases.

3. Waiting for "good" project to contribute

There is nothing like good or bad project that I learned over course of time. It's a problem and solution cycle for every software developer which we keep on focusing for. If you are looking for a good project or good company, you may wait for longer. 

To learn and experiment anything, do not wait for project to come to you that's a reactive approach, go ahead and create something from the technology which interests you.

4. Shy away from learning from Seniors

You will find many mentors in your career, try to gain as much knowlege you can gain from them. You need to be proactively discuss solutions and understanding with them. This will give you a different perspective and opportunity to align your thought process with the experienced folks.

You would argue that you can learn from courses, youtube or chatgpt. Well you should and you would learn from the internet but the conversations you will do with your mentors can not be replaced with internet for sure that I learned gradually. 

5. Hesitate to ask questions

I always stopped myself from asking the questions during discussions and meetings. Many times the question came to my mind was asked by someone else, and I regreted. Asking questions during presentations, pair programming, team meetings etc. rewards you with many things. Your team understands that you are interested to learn, you get required clarity, you gain confidence, the facilitator feels that you are engaged into the conversation so and so forth. 

However, on contrary if you don't ask, you mainly miss the easy opportunity to practice courage. When your inner  self is resisting and still you ask the question, see how it feels. Moreoever, you missed an opportunity of curiously seeking answers from the learned ones which you may not easily get answer of, on your own.

There are tons of other mistakes, but these 5 I believe could have turned me in a different direction initially, and those who are going to be software developers in the future may learn from them and avoid them as much as possible for better growth and satisfaction. 

Tuesday, July 16, 2024

Some useful stream operations

Separate Odd and Even numbers (List<Integer> to Map<String, List<Integer>>)

Map<String, List<Integer>> oddEvenMap = numbers.stream()  
        .collect(Collectors.groupingBy(i -> (i % 2 != 0 ? "Odd" : "Even")));

Find distinct values from Map<String, List<Integer>

Set<Integer> set = map.values().stream()
					.flatMap(Collection::stream)
					.collect(Collectors.toSet());

Biggest Odd and Even from List (List<Integer> to Map<String, Integer>)

Map<String, Integer> bigOddEven = numbers.stream().collect(Collectors.toMap(i -> (i % 2 == 0 ? "Even" : "Odd"),  Function.identity(), (i1 , i2) -> i1 > i2 ? i1 : i2));

Use value as key and create new map from existing

Map<String, Integer> map = new HashMap<>();  
map.put("a", 1);  
map.put("b", 1);  
map.put("c", 2);  
  
Map<Integer, List<String>> ans = map.keySet().stream().collect(Collectors.groupingBy(map::get));  
System.out.println(ans);

Output

{1=[a, b], 2=[c]}

Find frequency of each number in list

List<Integer> numbers = new ArrayList<>();  
numbers.add(1);  
numbers.add(2);  
numbers.add(3);  
numbers.add(4);  
numbers.add(4);  
  
Map<Integer, Long> ans = numbers.stream()  
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));  
System.out.println(ans);

Output

{1=1, 2=1, 3=1, 4=2}

Remove from map based on condition on values

Map<String, Integer> map = new HashMap<>();  
map.put("a", 1);  
map.put("b", 1);  
map.put("c", 2);  
  
map.values().removeIf(i -> i == 1);  
System.out.println(map);

Output

{c=2}

Distinct and Sum of numbers

Stream<Integer> distinct = numbers.stream().distinct();
int s = numbers.stream().mapToInt(Integer::intValue).sum();

IntSummaryStatistics for Average, Sum, Min/Max

IntSummaryStatistics intSummaryStatistics = numbers.stream().mapToInt(Integer::intValue).summaryStatistics();
double average = intSummaryStatistics.getAverage();
long sum = intSummaryStatistics.getSum();
int max = intSummaryStatistics.getMax();
int min = intSummaryStatistics.getMin();
long count = intSummaryStatistics.getCount();

Monday, June 3, 2024

Build SVG in nodeJS

Purpose

I noticed tags shown in Github with the help of link. That made me curious to know more about it. There are certain ways to do it but the interesting one I found was dynamically building and returing the SVG image which can directly render in markdown file. I came to know about Shields which builds beautiful dynamic badges as per the need.

The Way

I wanted to create a basic setup which can render the tag directly in the markdown file. The first step was to create a svg content. We can do it in many ways, however to create dynamic tags I decided to go with nodeJS.

Building svg from scratch is quite tricky, I got to know about Roughjs, which made things even simpler. Initial idea was to dynamically build tag as per the input sent in query parameter. It was a bit struggle initially to get it working. However, I came up with following function to achieve that.

With the help of monspace fonts, we can exactly calculate the width and height required for the tag. I tried to keep it as dynamically as possible.

const { DOMImplementation, XMLSerializer } = require('xmldom');
const xmlSerializer = new XMLSerializer();
const document = new DOMImplementation().createDocument('http://www.w3.org/1999/xhtml', 'html', null);

const rough = require('roughjs');

//...

function createSVG(text, color) {
    console.log(text);
    if(!text || text.length == 0) {
        text = '?';
    }

    if(text.length > maxTagSize) {
        text = text.substr(0, maxTagSize);
    }

    text = text.trim().toUpperCase();

    var textLength = text.length;

    var width = (fontWidth * textLength) + fontWidth;
    var height = fontSize * 1.5;

    var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    const rc = rough.svg(svg);
    
    var tagName = document.createElementNS("http://www.w3.org/2000/svg", "text");
    tagName.setAttribute("x", (fontWidth / 2));
    tagName.setAttribute("y", fontSize + 1);
    tagName.setAttribute('style', 'font-family:monospace;font-weight:bold;font-size:'+fontSize+'px;');
    tagName.textContent = text;

    var rectangle = rc.rectangle(0, 0, width , height, {roughness: 0, fill : color, fillStyle: 'solid', stroke: '#8BD8E2'});
    
    svg.setAttribute("width", width);
    svg.setAttribute("height", height);
    svg.appendChild(rectangle);
    svg.appendChild(tagName);
    return svg;
}

Later I deployed this on Google Cloud Function to make it accessible for markdown by supplying function URL in HTML image tag source as shown below.

    <img src="https://tags.akashthakare.com/generateTag?title=java" />

Next

This was a very interesting idea which I can publish and used in my own GitHub profile or even in readme files of repositories.

I would like to make it more interesting going forward. I am thinking to introduce icons or even images in it. This way it will give flexibility to caller of the function to build different styles of tag with different content.

This can be implemented without using RoughJS. I will see if I can do it without using it.

If you are interested, you can check the full code here.

Tuesday, May 28, 2024

Blogger API fetch drafts



Google provides APIs to integrate your system with existing Google services for various purpose. I started exploring APIs related to Blogger for this Blog to automate things and building a convinient system of writing and publishing blog. I came across with one scenario which I would like to share.

While fetching the drafts with the help of Blogger API v3, I realized the API was not returning the drafts. Even after supplying the post id or the status it was returning 404 Not Found.

draft_post = service.posts().get(blogId=blog_id, postId=post_id).execute()

This is an intended behavior from the API, for some reason the GET API is not returning the draft post. Following was a loose attempt to make it work, but failed. However, every other source was claiming this to be working, but wasn’t.

draft_post = service.posts().get(blogId=blog_id, postId=post_id, status='DRAFT').execute()

While looking for the solution in different sources, I found this 4 year old thread,

Unable to retrieve “scheduled” and “draft” posts while using the blooger v3 api

So, I had no option but to switch to this method of fetching the draft post, it’s not a cleaner way but still, nothing else I can do about it. The solution looks like this,

draft_posts = service.posts().list(blogId=blog_id, status='DRAFT').execute()

if 'items' in draft_posts:
    for post in draft_posts['items']:
        if post['id'] == post_id:
            print("Draft post found.")
else:
    print("No draft posts found")

Same thing applies to scheduled post as well, we have to fetch all the posts and filter respectively. It’s not something related to the python library, with other languages as well the same result is being returned.

I spend some time to identify why the API is not returning unpublished post with the help of GET API but couldn’t find any official clarification for the same. I feel, it’s by design not returning the unpublished ones to avoid any repercussions while doing some operations on the returned post considering them as published.

↑ Back to Top