URI 源碼分析

使用 URI 來校驗 url,如下代碼:

import org.apache.commons.validator.routines.UrlValidator;

import java.net.URI;
import java.net.URISyntaxException;

public class UrlUtils {

    public static void main(String[] args) {
        String url = "http://www.jiaobuchong.com?name=tom&request={\"hobby\":\"film\"}";
        System.out.println(new UrlValidator().isValid(url));
        isValidUrl(url);
    }

    public static boolean isValidUrl(String url) {
        try {
            URI uri = new URI(url);
            return true;
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return false;
    }
}

如果 url 含有 {}""則會拋出 URISyntaxException,校驗不通過。 看了一下 URI 裏面的源碼,有個校驗還挺有意思的。

一、分析 checkChar 方法

1、通過 highMask 獲取合法字符範圍的二進制

private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA;
private static final long L_LOWALPHA = 0L;
private static final long L_UPALPHA = 0L;

private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA;
private static final long H_LOWALPHA = highMask('a', 'z');
private static final long H_UPALPHA = highMask('A', 'Z');
checkChar(0, L_ALPHA, H_ALPHA, "scheme name");

L_ALPHA 與的結果是 0,下面就要開始比較有意思的部分了,來看 highMask 到底做了什麼:

    // Compute a high-order mask for the characters
    // between first and last, inclusive
    private static long highMask(char first, char last) {
        long m = 0;
        // Math.min 表示從 ASCII 的範圍裏取值
        // Math.max(Math.min(first, 127), 64) 表示在 ASCII 碼錶[64, 127]之間的字符
        // 減去 64 表示不包括 64 這個字符,相對 64 之間還有多少個個字符
        int f = Math.max(Math.min(first, 127), 64) - 64;
        int l = Math.max(Math.min(last, 127), 64) - 64;
        for (int i = f; i <= l; i++)
            m |= 1L << i;
        return m;
    }

highMask(‘a’, ‘z’ ) m 的二進制:
在這裏插入圖片描述
highMask(‘A’, ‘Z’ ) m 的二進制:
在這裏插入圖片描述
H_ALPHA = H_LOWALPHA | H_UPALPHA 相與的結果是:
11111111111111111111111111000000111111111111111111111111110,相與之後的結果 H_UPALPHA 表示 a-zA-Z 這個範圍的字符,一個蘿蔔一個坑,不在這個範圍的數據對應的坑位就是 0。

2、match 方法

   // Tell whether the given character is permitted by the given mask pair
    private static boolean match(char c, long lowMask, long highMask) {
        if (c == 0) // 0 doesn't have a slot in the mask. So, it never matches.
            return false;
        if (c < 64)
            return ((1L << c) & lowMask) != 0;
        if (c < 128)
            // 左移 (c - 64) 位,和 highMask 進行 & 運算,如果不等於 0 就表示這個字符是合法的
            return ((1L << (c - 64)) & highMask) != 0;
        return false;
    }

二、分析 checkChars 方法

通過上面的分析,URI 判斷是否合法字符的原理就是:將和合法範圍的字符轉成一個二進制數,然後將傳進來的字符和這個二進制進行與運算,如果不等於 0 就表示這個字符是合法的。

再來分析一下這個方法:

checkChars(1, p, L_SCHEME, H_SCHEME, "scheme name");

通過代碼中的計算,L_SCHEME 的結果是:

0 | lowMask('0', '9') | lowMask("+-.")

H_SCHEME:

H_SCHEME = highMask('a', 'z') | highMask('A', 'Z') | 0 | highMask("+-.")

來看 lowMask的代碼,生成的結果 m 表示 [0-9] 二進制的範圍:

    // Compute a low-order mask for the characters
    // between first and last, inclusive
    private static long lowMask(char first, char last) {
        long m = 0;
        int f = Math.max(Math.min(first, 63), 0);
        int l = Math.max(Math.min(last, 63), 0);
        for (int i = f; i <= l; i++)
            m |= 1L << i;
        return m;
    }

然後 在 match 方法裏:

    // Tell whether the given character is permitted by the given mask pair
    private static boolean match(char c, long lowMask, long highMask) {
        if (c == 0) // 0 doesn't have a slot in the mask. So, it never matches.
            return false;
        if (c < 64)
            // 對於小於 64 的字符,和 lowMask 進行與運算,不等於0表示合法的字符
            return ((1L << c) & lowMask) != 0;
        if (c < 128)
            return ((1L << (c - 64)) & highMask) != 0;
        return false;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章