Skip to content

Commit

Permalink
Added unchecked casts; fixes bug #441. Code and original patches supp…
Browse files Browse the repository at this point in the history
…lied by @stuarthalloway

Signed-off-by: Rich Hickey <richhickey@gmail.com>
  • Loading branch information
abedra authored and richhickey committed Nov 26, 2010
1 parent 8225407 commit cbd789d
Show file tree
Hide file tree
Showing 3 changed files with 343 additions and 8 deletions.
54 changes: 47 additions & 7 deletions src/clj/clojure/core.clj
Expand Up @@ -3031,6 +3031,49 @@
:added "1.0"} :added "1.0"}
[x] (clojure.lang.RT/booleanCast x)) [x] (clojure.lang.RT/booleanCast x))


(defn unchecked-byte
"Coerce to byte. Subject to rounding or truncation."
{:inline (fn [x] `(. clojure.lang.RT (uncheckedByteCast ~x)))
:added "1.3"}
[^Number x] (clojure.lang.RT/uncheckedByteCast x))

(defn unchecked-short
"Coerce to short. Subject to rounding or truncation."
{:inline (fn [x] `(. clojure.lang.RT (uncheckedShortCast ~x)))
:added "1.3"}
[^Number x] (clojure.lang.RT/uncheckedShortCast x))

(defn unchecked-char
"Coerce to char. Subject to rounding or truncation."
{:inline (fn [x] `(. clojure.lang.RT (uncheckedCharCast ~x)))
:added "1.3"}
[x] (. clojure.lang.RT (uncheckedCharCast x)))

(defn unchecked-int
"Coerce to int. Subject to rounding or truncation."
{:inline (fn [x] `(. clojure.lang.RT (uncheckedIntCast ~x)))
:added "1.3"}
[^Number x] (clojure.lang.RT/uncheckedIntCast x))

(defn unchecked-long
"Coerce to long. Subject to rounding or truncation."
{:inline (fn [x] `(. clojure.lang.RT (uncheckedLongCast ~x)))
:added "1.3"}
[^Number x] (clojure.lang.RT/uncheckedLongCast x))

(defn unchecked-float
"Coerce to float. Subject to rounding."
{:inline (fn [x] `(. clojure.lang.RT (uncheckedFloatCast ~x)))
:added "1.3"}
[^Number x] (clojure.lang.RT/uncheckedFloatCast x))

(defn unchecked-double
"Coerce to double. Subject to rounding."
{:inline (fn [x] `(. clojure.lang.RT (uncheckedDoubleCast ~x)))
:added "1.3"}
[^Number x] (clojure.lang.RT/uncheckedDoubleCast x))


(defn number? (defn number?
"Returns true if x is a Number" "Returns true if x is a Number"
{:added "1.0" {:added "1.0"
Expand Down Expand Up @@ -5832,14 +5875,11 @@
{:added "1.0"} {:added "1.0"}
([f & opts] ([f & opts]
(let [opts (normalize-slurp-opts opts) (let [opts (normalize-slurp-opts opts)
sb (StringBuilder.)] sb (StringBuilder.)
sw (java.io.StringWriter.)]
(with-open [#^java.io.Reader r (apply jio/reader f opts)] (with-open [#^java.io.Reader r (apply jio/reader f opts)]
(loop [c (.read r)] (jio/copy r sw)
(if (neg? c) (str sw)))))
(str sb)
(do
(.append sb (char c))
(recur (.read r)))))))))


(defn spit (defn spit
"Opposite of slurp. Opens f with writer, writes content, then "Opposite of slurp. Opens f with writer, writes content, then
Expand Down
208 changes: 208 additions & 0 deletions src/jvm/clojure/lang/RT.java
Expand Up @@ -1055,6 +1055,214 @@ static public double doubleCast(double x){
return x; return x;
} }


static public byte uncheckedByteCast(Object x){
return ((Number) x).byteValue();
}

static public byte uncheckedByteCast(byte x){
return x;
}

static public byte uncheckedByteCast(short x){
return (byte) x;
}

static public byte uncheckedByteCast(int x){
return (byte) x;
}

static public byte uncheckedByteCast(long x){
return (byte) x;
}

static public byte uncheckedByteCast(float x){
return (byte) x;
}

static public byte uncheckedByteCast(double x){
return (byte) x;
}

static public short uncheckedShortCast(Object x){
return ((Number) x).shortValue();
}

static public short uncheckedShortCast(byte x){
return x;
}

static public short uncheckedShortCast(short x){
return x;
}

static public short uncheckedShortCast(int x){
return (short) x;
}

static public short uncheckedShortCast(long x){
return (short) x;
}

static public short uncheckedShortCast(float x){
return (short) x;
}

static public short uncheckedShortCast(double x){
return (short) x;
}

static public char uncheckedCharCast(Object x){
if(x instanceof Character)
return ((Character) x).charValue();
return (char) ((Number) x).longValue();
}

static public char uncheckedCharCast(byte x){
return (char) x;
}

static public char uncheckedCharCast(short x){
return (char) x;
}

static public char uncheckedCharCast(char x){
return x;
}

static public char uncheckedCharCast(int x){
return (char) x;
}

static public char uncheckedCharCast(long x){
return (char) x;
}

static public char uncheckedCharCast(float x){
return (char) x;
}

static public char uncheckedCharCast(double x){
return (char) x;
}

static public int uncheckedIntCast(Object x){
if(x instanceof Number)
return ((Number)x).intValue();
return ((Character) x).charValue();
}

static public int uncheckedIntCast(byte x){
return x;
}

static public int uncheckedIntCast(short x){
return x;
}

static public int uncheckedIntCast(char x){
return x;
}

static public int uncheckedIntCast(int x){
return x;
}

static public int uncheckedIntCast(long x){
return (int) x;
}

static public int uncheckedIntCast(float x){
return (int) x;
}

static public int uncheckedIntCast(double x){
return (int) x;
}

static public long uncheckedLongCast(Object x){
return ((Number) x).longValue();
}

static public long uncheckedLongCast(byte x){
return x;
}

static public long uncheckedLongCast(short x){
return x;
}

static public long uncheckedLongCast(int x){
return x;
}

static public long uncheckedLongCast(long x){
return x;
}

static public long uncheckedLongCast(float x){
return (long) x;
}

static public long uncheckedLongCast(double x){
return (long) x;
}

static public float uncheckedFloatCast(Object x){
return ((Number) x).floatValue();
}

static public float uncheckedFloatCast(byte x){
return x;
}

static public float uncheckedFloatCast(short x){
return x;
}

static public float uncheckedFloatCast(int x){
return x;
}

static public float uncheckedFloatCast(long x){
return x;
}

static public float uncheckedFloatCast(float x){
return x;
}

static public float uncheckedFloatCast(double x){
return (float) x;
}

static public double uncheckedDoubleCast(Object x){
return ((Number) x).doubleValue();
}

static public double uncheckedDoubleCast(byte x){
return x;
}

static public double uncheckedDoubleCast(short x){
return x;
}

static public double uncheckedDoubleCast(int x){
return x;
}

static public double uncheckedDoubleCast(long x){
return x;
}

static public double uncheckedDoubleCast(float x){
return x;
}

static public double uncheckedDoubleCast(double x){
return x;
}

static public IPersistentMap map(Object... init){ static public IPersistentMap map(Object... init){
if(init == null) if(init == null)
return PersistentArrayMap.EMPTY; return PersistentArrayMap.EMPTY;
Expand Down
89 changes: 88 additions & 1 deletion test/clojure/test_clojure/numbers.clj
Expand Up @@ -12,7 +12,8 @@
;; ;;


(ns clojure.test-clojure.numbers (ns clojure.test-clojure.numbers
(:use clojure.test)) (:use clojure.test
clojure.template))




; TODO: ; TODO:
Expand All @@ -37,6 +38,91 @@
13178456923875639284562345789M 13178456923875639284562345789M
13178456923875639284562345789N)) 13178456923875639284562345789N))


(deftest unchecked-cast-num-obj
(do-template [prim-array cast]
(are [n]
(let [a (prim-array 1)]
(aset a 0 (cast n)))
(Byte. Byte/MAX_VALUE)
(Short. Short/MAX_VALUE)
(Integer. Integer/MAX_VALUE)
(Long. Long/MAX_VALUE)
(Float. Float/MAX_VALUE)
(Double. Double/MAX_VALUE))
byte-array
unchecked-byte
short-array
unchecked-short
char-array
unchecked-char
int-array
unchecked-int
long-array
unchecked-long
float-array
unchecked-float
double-array
unchecked-double))

(deftest unchecked-cast-num-prim
(do-template [prim-array cast]
(are [n]
(let [a (prim-array 1)]
(aset a 0 (cast n)))
Byte/MAX_VALUE
Short/MAX_VALUE
Integer/MAX_VALUE
Long/MAX_VALUE
Float/MAX_VALUE
Double/MAX_VALUE)
byte-array
unchecked-byte
short-array
unchecked-short
char-array
unchecked-char
int-array
unchecked-int
long-array
unchecked-long
float-array
unchecked-float
double-array
unchecked-double))

(deftest unchecked-cast-char
; in keeping with the checked cast functions, char and Character can only be cast to int
(is (unchecked-int (char 0xFFFF)))
(is (let [c (char 0xFFFF)] (unchecked-int c)))) ; force primitive char

(def expected-casts
[
[:input [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE Long/MAX_VALUE Float/MAX_VALUE Double/MAX_VALUE]]
[char [:error (char 0) (char 1) (char 127) (char 32767) :error :error :error :error]]
[unchecked-char [(char 65535) (char 0) (char 1) (char 127) (char 32767) (char 65535) (char 65535) (char 65535) (char 65535)]]
[byte [-1 0 1 Byte/MAX_VALUE :error :error :error :error :error]]
[unchecked-byte [-1 0 1 Byte/MAX_VALUE -1 -1 -1 -1 -1]]
[short [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE :error :error :error :error]]
[unchecked-short [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE -1 -1 -1 -1]]
[int [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE :error :error :error]]
[unchecked-int [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE -1 Integer/MAX_VALUE Integer/MAX_VALUE]]
[long [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE Long/MAX_VALUE Long/MAX_VALUE Long/MAX_VALUE]]
[unchecked-long [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE Long/MAX_VALUE Long/MAX_VALUE Long/MAX_VALUE]]
;; 2.14748365E9 if when float/double conversion is avoided...
[float [-1.0 0.0 1.0 127.0 32767.0 2.147483648E9 9.223372036854776E18 Float/MAX_VALUE :error]]
[unchecked-float [-1.0 0.0 1.0 127.0 32767.0 2.147483648E9 9.223372036854776E18 Float/MAX_VALUE Float/POSITIVE_INFINITY]]
[double [-1.0 0.0 1.0 127.0 32767.0 2.147483647E9 9.223372036854776E18 Float/MAX_VALUE Double/MAX_VALUE]]
[unchecked-double [-1.0 0.0 1.0 127.0 32767.0 2.147483647E9 9.223372036854776E18 Float/MAX_VALUE Double/MAX_VALUE]]])

(deftest test-expected-casts
(let [[[_ inputs] & expectations] expected-casts]
(doseq [[f vals] expectations]
(let [wrapped (fn [x]
(try
(f x)
(catch IllegalArgumentException e :error)))]
(is (= vals (map wrapped inputs)))))))

;; *** Functions *** ;; *** Functions ***


(defonce DELTA 1e-12) (defonce DELTA 1e-12)
Expand Down Expand Up @@ -394,3 +480,4 @@ Math/pow overflows to Infinity."
(is (== (numerator 1/2) 1)) (is (== (numerator 1/2) 1))
(is (= (bigint (/ 100000000000000000000 3)) 33333333333333333333)) (is (= (bigint (/ 100000000000000000000 3)) 33333333333333333333))
(is (= (long 10000000000000000000/3) 3333333333333333333))) (is (= (long 10000000000000000000/3) 3333333333333333333)))

0 comments on commit cbd789d

Please sign in to comment.