diff options
Diffstat (limited to 'luni/src/main/native')
24 files changed, 11568 insertions, 0 deletions
diff --git a/luni/src/main/native/cbigint.c b/luni/src/main/native/cbigint.c new file mode 100644 index 0000000..0f65ca0 --- /dev/null +++ b/luni/src/main/native/cbigint.c @@ -0,0 +1,875 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> +#include "cbigint.h" + +#if defined(LINUX) || defined(FREEBSD) +#define USE_LL +#endif + +#ifdef HY_LITTLE_ENDIAN +#define at(i) (i) +#else +#define at(i) ((i)^1) +/* the sequence for halfAt is -1, 2, 1, 4, 3, 6, 5, 8... */ +/* and it should correspond to 0, 1, 2, 3, 4, 5, 6, 7... */ +#define halfAt(i) (-((-(i)) ^ 1)) +#endif + +#define HIGH_IN_U64(u64) ((u64) >> 32) +#if defined(USE_LL) +#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFFLL) +#else +#if defined(USE_L) +#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFFL) +#else +#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFF) +#endif /* USE_L */ +#endif /* USE_LL */ + +#if defined(USE_LL) +#define TEN_E1 (0xALL) +#define TEN_E2 (0x64LL) +#define TEN_E3 (0x3E8LL) +#define TEN_E4 (0x2710LL) +#define TEN_E5 (0x186A0LL) +#define TEN_E6 (0xF4240LL) +#define TEN_E7 (0x989680LL) +#define TEN_E8 (0x5F5E100LL) +#define TEN_E9 (0x3B9ACA00LL) +#define TEN_E19 (0x8AC7230489E80000LL) +#else +#if defined(USE_L) +#define TEN_E1 (0xAL) +#define TEN_E2 (0x64L) +#define TEN_E3 (0x3E8L) +#define TEN_E4 (0x2710L) +#define TEN_E5 (0x186A0L) +#define TEN_E6 (0xF4240L) +#define TEN_E7 (0x989680L) +#define TEN_E8 (0x5F5E100L) +#define TEN_E9 (0x3B9ACA00L) +#define TEN_E19 (0x8AC7230489E80000L) +#else +#define TEN_E1 (0xA) +#define TEN_E2 (0x64) +#define TEN_E3 (0x3E8) +#define TEN_E4 (0x2710) +#define TEN_E5 (0x186A0) +#define TEN_E6 (0xF4240) +#define TEN_E7 (0x989680) +#define TEN_E8 (0x5F5E100) +#define TEN_E9 (0x3B9ACA00) +#define TEN_E19 (0x8AC7230489E80000) +#endif /* USE_L */ +#endif /* USE_LL */ + +#define TIMES_TEN(x) (((x) << 3) + ((x) << 1)) +#define bitSection(x, mask, shift) (((x) & (mask)) >> (shift)) +#define DOUBLE_TO_LONGBITS(dbl) (*((U_64 *)(&dbl))) +#define FLOAT_TO_INTBITS(flt) (*((U_32 *)(&flt))) +#define CREATE_DOUBLE_BITS(normalizedM, e) (((normalizedM) & MANTISSA_MASK) | (((U_64)((e) + E_OFFSET)) << 52)) + +#if defined(USE_LL) +#define MANTISSA_MASK (0x000FFFFFFFFFFFFFLL) +#define EXPONENT_MASK (0x7FF0000000000000LL) +#define NORMAL_MASK (0x0010000000000000LL) +#define SIGN_MASK (0x8000000000000000LL) +#else +#if defined(USE_L) +#define MANTISSA_MASK (0x000FFFFFFFFFFFFFL) +#define EXPONENT_MASK (0x7FF0000000000000L) +#define NORMAL_MASK (0x0010000000000000L) +#define SIGN_MASK (0x8000000000000000L) +#else +#define MANTISSA_MASK (0x000FFFFFFFFFFFFF) +#define EXPONENT_MASK (0x7FF0000000000000) +#define NORMAL_MASK (0x0010000000000000) +#define SIGN_MASK (0x8000000000000000) +#endif /* USE_L */ +#endif /* USE_LL */ + +#define E_OFFSET (1075) + +#define FLOAT_MANTISSA_MASK (0x007FFFFF) +#define FLOAT_EXPONENT_MASK (0x7F800000) +#define FLOAT_NORMAL_MASK (0x00800000) +#define FLOAT_E_OFFSET (150) + +IDATA +simpleAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2) +{ + /* assumes length > 0 */ + IDATA index = 1; + + *arg1 += arg2; + if (arg2 <= *arg1) + return 0; + else if (length == 1) + return 1; + + while (++arg1[index] == 0 && ++index < length); + + return (IDATA) index == length; +} + +IDATA +addHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2) +{ + /* addition is limited by length of arg1 as it this function is + * storing the result in arg1 */ + /* fix for cc (GCC) 3.2 20020903 (Red Hat Linux 8.0 3.2-7): code generated does not + * do the temp1 + temp2 + carry addition correct. carry is 64 bit because gcc has + * subtle issues when you mix 64 / 32 bit maths. */ + U_64 temp1, temp2, temp3; /* temporary variables to help the SH-4, and gcc */ + U_64 carry; + IDATA index; + + if (length1 == 0 || length2 == 0) + { + return 0; + } + else if (length1 < length2) + { + length2 = length1; + } + + carry = 0; + index = 0; + do + { + temp1 = arg1[index]; + temp2 = arg2[index]; + temp3 = temp1 + temp2; + arg1[index] = temp3 + carry; + if (arg2[index] < arg1[index]) + carry = 0; + else if (arg2[index] != arg1[index]) + carry = 1; + } + while (++index < length2); + if (!carry) + return 0; + else if (index == length1) + return 1; + + while (++arg1[index] == 0 && ++index < length1); + + return (IDATA) index == length1; +} + +void +subtractHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2) +{ + /* assumes arg1 > arg2 */ + IDATA index; + for (index = 0; index < length1; ++index) + arg1[index] = ~arg1[index]; + simpleAddHighPrecision (arg1, length1, 1); + + while (length2 > 0 && arg2[length2 - 1] == 0) + --length2; + + addHighPrecision (arg1, length1, arg2, length2); + + for (index = 0; index < length1; ++index) + arg1[index] = ~arg1[index]; + simpleAddHighPrecision (arg1, length1, 1); +} + +U_32 +simpleMultiplyHighPrecision (U_64 * arg1, IDATA length, U_64 arg2) +{ + /* assumes arg2 only holds 32 bits of information */ + U_64 product; + IDATA index; + + index = 0; + product = 0; + + do + { + product = + HIGH_IN_U64 (product) + arg2 * LOW_U32_FROM_PTR (arg1 + index); + LOW_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (product); + product = + HIGH_IN_U64 (product) + arg2 * HIGH_U32_FROM_PTR (arg1 + index); + HIGH_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (product); + } + while (++index < length); + + return HIGH_U32_FROM_VAR (product); +} + +void +simpleMultiplyAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2, + U_32 * result) +{ + /* Assumes result can hold the product and arg2 only holds 32 bits + of information */ + U_64 product; + IDATA index, resultIndex; + + index = resultIndex = 0; + product = 0; + + do + { + product = + HIGH_IN_U64 (product) + result[at (resultIndex)] + + arg2 * LOW_U32_FROM_PTR (arg1 + index); + result[at (resultIndex)] = LOW_U32_FROM_VAR (product); + ++resultIndex; + product = + HIGH_IN_U64 (product) + result[at (resultIndex)] + + arg2 * HIGH_U32_FROM_PTR (arg1 + index); + result[at (resultIndex)] = LOW_U32_FROM_VAR (product); + ++resultIndex; + } + while (++index < length); + + result[at (resultIndex)] += HIGH_U32_FROM_VAR (product); + if (result[at (resultIndex)] < HIGH_U32_FROM_VAR (product)) + { + /* must be careful with ++ operator and macro expansion */ + ++resultIndex; + while (++result[at (resultIndex)] == 0) + ++resultIndex; + } +} + +void +multiplyHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2, + U_64 * result, IDATA length) +{ + /* assumes result is large enough to hold product */ + U_64 *temp; + U_32 *resultIn32; + IDATA count, index; + + if (length1 < length2) + { + temp = arg1; + arg1 = arg2; + arg2 = temp; + count = length1; + length1 = length2; + length2 = count; + } + + memset (result, 0, sizeof (U_64) * length); + + /* length1 > length2 */ + resultIn32 = (U_32 *) result; + index = -1; + for (count = 0; count < length2; ++count) + { + simpleMultiplyAddHighPrecision (arg1, length1, LOW_IN_U64 (arg2[count]), + resultIn32 + (++index)); + simpleMultiplyAddHighPrecision (arg1, length1, + HIGH_IN_U64 (arg2[count]), + resultIn32 + (++index)); + + } +} + +U_32 +simpleAppendDecimalDigitHighPrecision (U_64 * arg1, IDATA length, U_64 digit) +{ + /* assumes digit is less than 32 bits */ + U_64 arg; + IDATA index = 0; + + digit <<= 32; + do + { + arg = LOW_IN_U64 (arg1[index]); + digit = HIGH_IN_U64 (digit) + TIMES_TEN (arg); + LOW_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (digit); + + arg = HIGH_IN_U64 (arg1[index]); + digit = HIGH_IN_U64 (digit) + TIMES_TEN (arg); + HIGH_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (digit); + } + while (++index < length); + + return HIGH_U32_FROM_VAR (digit); +} + +void +simpleShiftLeftHighPrecision (U_64 * arg1, IDATA length, IDATA arg2) +{ + /* assumes length > 0 */ + IDATA index, offset; + if (arg2 >= 64) + { + offset = arg2 >> 6; + index = length; + + while (--index - offset >= 0) + arg1[index] = arg1[index - offset]; + do + { + arg1[index] = 0; + } + while (--index >= 0); + + arg2 &= 0x3F; + } + + if (arg2 == 0) + return; + while (--length > 0) + { + arg1[length] = arg1[length] << arg2 | arg1[length - 1] >> (64 - arg2); + } + *arg1 <<= arg2; +} + +IDATA +highestSetBit (U_64 * y) +{ + U_32 x; + IDATA result; + + if (*y == 0) + return 0; + +#if defined(USE_LL) + if (*y & 0xFFFFFFFF00000000LL) + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } + else + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } +#else +#if defined(USE_L) + if (*y & 0xFFFFFFFF00000000L) + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } + else + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } +#else + if (*y & 0xFFFFFFFF00000000) + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } + else + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } +#endif /* USE_L */ +#endif /* USE_LL */ + + if (x & 0xFFFF0000) + { + x = bitSection (x, 0xFFFF0000, 16); + result += 16; + } + if (x & 0xFF00) + { + x = bitSection (x, 0xFF00, 8); + result += 8; + } + if (x & 0xF0) + { + x = bitSection (x, 0xF0, 4); + result += 4; + } + if (x > 0x7) + return result + 4; + else if (x > 0x3) + return result + 3; + else if (x > 0x1) + return result + 2; + else + return result + 1; +} + +IDATA +lowestSetBit (U_64 * y) +{ + U_32 x; + IDATA result; + + if (*y == 0) + return 0; + +#if defined(USE_LL) + if (*y & 0x00000000FFFFFFFFLL) + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } + else + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } +#else +#if defined(USE_L) + if (*y & 0x00000000FFFFFFFFL) + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } + else + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } +#else + if (*y & 0x00000000FFFFFFFF) + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } + else + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } +#endif /* USE_L */ +#endif /* USE_LL */ + + if (!(x & 0xFFFF)) + { + x = bitSection (x, 0xFFFF0000, 16); + result += 16; + } + if (!(x & 0xFF)) + { + x = bitSection (x, 0xFF00, 8); + result += 8; + } + if (!(x & 0xF)) + { + x = bitSection (x, 0xF0, 4); + result += 4; + } + + if (x & 0x1) + return result + 1; + else if (x & 0x2) + return result + 2; + else if (x & 0x4) + return result + 3; + else + return result + 4; +} + +IDATA +highestSetBitHighPrecision (U_64 * arg, IDATA length) +{ + IDATA highBit; + + while (--length >= 0) + { + highBit = highestSetBit (arg + length); + if (highBit) + return highBit + 64 * length; + } + + return 0; +} + +IDATA +lowestSetBitHighPrecision (U_64 * arg, IDATA length) +{ + IDATA lowBit, index = -1; + + while (++index < length) + { + lowBit = lowestSetBit (arg + index); + if (lowBit) + return lowBit + 64 * index; + } + + return 0; +} + +IDATA +compareHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2) +{ + while (--length1 >= 0 && arg1[length1] == 0); + while (--length2 >= 0 && arg2[length2] == 0); + + if (length1 > length2) + return 1; + else if (length1 < length2) + return -1; + else if (length1 > -1) + { + do + { + if (arg1[length1] > arg2[length1]) + return 1; + else if (arg1[length1] < arg2[length1]) + return -1; + } + while (--length1 >= 0); + } + + return 0; +} + +jdouble +toDoubleHighPrecision (U_64 * arg, IDATA length) +{ + IDATA highBit; + U_64 mantissa, test64; + U_32 test; + jdouble result; + + while (length > 0 && arg[length - 1] == 0) + --length; + + if (length == 0) + result = 0.0; + else if (length > 16) + { + DOUBLE_TO_LONGBITS (result) = EXPONENT_MASK; + } + else if (length == 1) + { + highBit = highestSetBit (arg); + if (highBit <= 53) + { + highBit = 53 - highBit; + mantissa = *arg << highBit; + DOUBLE_TO_LONGBITS (result) = + CREATE_DOUBLE_BITS (mantissa, -highBit); + } + else + { + highBit -= 53; + mantissa = *arg >> highBit; + DOUBLE_TO_LONGBITS (result) = + CREATE_DOUBLE_BITS (mantissa, highBit); + + /* perform rounding, round to even in case of tie */ + test = (LOW_U32_FROM_PTR (arg) << (11 - highBit)) & 0x7FF; + if (test > 0x400 || ((test == 0x400) && (mantissa & 1))) + DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1; + } + } + else + { + highBit = highestSetBit (arg + (--length)); + if (highBit <= 53) + { + highBit = 53 - highBit; + if (highBit > 0) + { + mantissa = + (arg[length] << highBit) | (arg[length - 1] >> + (64 - highBit)); + } + else + { + mantissa = arg[length]; + } + DOUBLE_TO_LONGBITS (result) = + CREATE_DOUBLE_BITS (mantissa, length * 64 - highBit); + + /* perform rounding, round to even in case of tie */ + test64 = arg[--length] << highBit; + if (test64 > SIGN_MASK || ((test64 == SIGN_MASK) && (mantissa & 1))) + DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1; + else if (test64 == SIGN_MASK) + { + while (--length >= 0) + { + if (arg[length] != 0) + { + DOUBLE_TO_LONGBITS (result) = + DOUBLE_TO_LONGBITS (result) + 1; + break; + } + } + } + } + else + { + highBit -= 53; + mantissa = arg[length] >> highBit; + DOUBLE_TO_LONGBITS (result) = + CREATE_DOUBLE_BITS (mantissa, length * 64 + highBit); + + /* perform rounding, round to even in case of tie */ + test = (LOW_U32_FROM_PTR (arg + length) << (11 - highBit)) & 0x7FF; + if (test > 0x400 || ((test == 0x400) && (mantissa & 1))) + DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1; + else if (test == 0x400) + { + do + { + if (arg[--length] != 0) + { + DOUBLE_TO_LONGBITS (result) = + DOUBLE_TO_LONGBITS (result) + 1; + break; + } + } + while (length > 0); + } + } + } + + return result; +} + +IDATA +tenToTheEHighPrecision (U_64 * result, IDATA length, jint e) +{ + /* size test */ + if (length < ((e / 19) + 1)) + return 0; + + memset (result, 0, length * sizeof (U_64)); + *result = 1; + + if (e == 0) + return 1; + + length = 1; + length = timesTenToTheEHighPrecision (result, length, e); + /* bad O(n) way of doing it, but simple */ + /* + do { + overflow = simpleAppendDecimalDigitHighPrecision(result, length, 0); + if (overflow) + result[length++] = overflow; + } while (--e); + */ + return length; +} + +IDATA +timesTenToTheEHighPrecision (U_64 * result, IDATA length, jint e) +{ + /* assumes result can hold value */ + U_64 overflow; + int exp10 = e; + + if (e == 0) + return length; + + /* bad O(n) way of doing it, but simple */ + /* + do { + overflow = simpleAppendDecimalDigitHighPrecision(result, length, 0); + if (overflow) + result[length++] = overflow; + } while (--e); + */ + /* Replace the current implementaion which performs a + * "multiplication" by 10 e number of times with an actual + * mulitplication. 10e19 is the largest exponent to the power of ten + * that will fit in a 64-bit integer, and 10e9 is the largest exponent to + * the power of ten that will fit in a 64-bit integer. Not sure where the + * break-even point is between an actual multiplication and a + * simpleAappendDecimalDigit() so just pick 10e3 as that point for + * now. + */ + while (exp10 >= 19) + { + overflow = simpleMultiplyHighPrecision64 (result, length, TEN_E19); + if (overflow) + result[length++] = overflow; + exp10 -= 19; + } + while (exp10 >= 9) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E9); + if (overflow) + result[length++] = overflow; + exp10 -= 9; + } + if (exp10 == 0) + return length; + else if (exp10 == 1) + { + overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 2) + { + overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0); + if (overflow) + result[length++] = overflow; + overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 3) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E3); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 4) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E4); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 5) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E5); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 6) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E6); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 7) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E7); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 8) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E8); + if (overflow) + result[length++] = overflow; + } + return length; +} + +U_64 +doubleMantissa (jdouble z) +{ + U_64 m = DOUBLE_TO_LONGBITS (z); + + if ((m & EXPONENT_MASK) != 0) + m = (m & MANTISSA_MASK) | NORMAL_MASK; + else + m = (m & MANTISSA_MASK); + + return m; +} + +IDATA +doubleExponent (jdouble z) +{ + /* assumes positive double */ + IDATA k = HIGH_U32_FROM_VAR (z) >> 20; + + if (k) + k -= E_OFFSET; + else + k = 1 - E_OFFSET; + + return k; +} + +UDATA +floatMantissa (jfloat z) +{ + UDATA m = (UDATA) FLOAT_TO_INTBITS (z); + + if ((m & FLOAT_EXPONENT_MASK) != 0) + m = (m & FLOAT_MANTISSA_MASK) | FLOAT_NORMAL_MASK; + else + m = (m & FLOAT_MANTISSA_MASK); + + return m; +} + +IDATA +floatExponent (jfloat z) +{ + /* assumes positive float */ + IDATA k = FLOAT_TO_INTBITS (z) >> 23; + if (k) + k -= FLOAT_E_OFFSET; + else + k = 1 - FLOAT_E_OFFSET; + + return k; +} + +/* Allow a 64-bit value in arg2 */ +U_64 +simpleMultiplyHighPrecision64 (U_64 * arg1, IDATA length, U_64 arg2) +{ + U_64 intermediate, *pArg1, carry1, carry2, prod1, prod2, sum; + IDATA index; + U_32 buf32; + + index = 0; + intermediate = 0; + pArg1 = arg1 + index; + carry1 = carry2 = 0; + + do + { + if ((*pArg1 != 0) || (intermediate != 0)) + { + prod1 = + (U_64) LOW_U32_FROM_VAR (arg2) * (U_64) LOW_U32_FROM_PTR (pArg1); + sum = intermediate + prod1; + if ((sum < prod1) || (sum < intermediate)) + { + carry1 = 1; + } + else + { + carry1 = 0; + } + prod1 = + (U_64) LOW_U32_FROM_VAR (arg2) * (U_64) HIGH_U32_FROM_PTR (pArg1); + prod2 = + (U_64) HIGH_U32_FROM_VAR (arg2) * (U_64) LOW_U32_FROM_PTR (pArg1); + intermediate = carry2 + HIGH_IN_U64 (sum) + prod1 + prod2; + if ((intermediate < prod1) || (intermediate < prod2)) + { + carry2 = 1; + } + else + { + carry2 = 0; + } + LOW_U32_FROM_PTR (pArg1) = LOW_U32_FROM_VAR (sum); + buf32 = HIGH_U32_FROM_PTR (pArg1); + HIGH_U32_FROM_PTR (pArg1) = LOW_U32_FROM_VAR (intermediate); + intermediate = carry1 + HIGH_IN_U64 (intermediate) + + (U_64) HIGH_U32_FROM_VAR (arg2) * (U_64) buf32; + } + pArg1++; + } + while (++index < length); + return intermediate; +} diff --git a/luni/src/main/native/cbigint.h b/luni/src/main/native/cbigint.h new file mode 100644 index 0000000..220aba8 --- /dev/null +++ b/luni/src/main/native/cbigint.h @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(cbigint_h) +#define cbigint_h +#include "fltconst.h" +#include "JNIHelp.h" +#define LOW_U32_FROM_VAR(u64) LOW_U32_FROM_LONG64(u64) +#define LOW_U32_FROM_PTR(u64ptr) LOW_U32_FROM_LONG64_PTR(u64ptr) +#define HIGH_U32_FROM_VAR(u64) HIGH_U32_FROM_LONG64(u64) +#define HIGH_U32_FROM_PTR(u64ptr) HIGH_U32_FROM_LONG64_PTR(u64ptr) +#if defined(__cplusplus) +extern "C" +{ +#endif + void multiplyHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, + IDATA length2, U_64 * result, IDATA length); + U_32 simpleAppendDecimalDigitHighPrecision (U_64 * arg1, IDATA length, + U_64 digit); + jdouble toDoubleHighPrecision (U_64 * arg, IDATA length); + IDATA tenToTheEHighPrecision (U_64 * result, IDATA length, jint e); + U_64 doubleMantissa (jdouble z); + IDATA compareHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, + IDATA length2); + IDATA highestSetBitHighPrecision (U_64 * arg, IDATA length); + void subtractHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, + IDATA length2); + IDATA doubleExponent (jdouble z); + U_32 simpleMultiplyHighPrecision (U_64 * arg1, IDATA length, U_64 arg2); + IDATA addHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, + IDATA length2); + void simpleMultiplyAddHighPrecisionBigEndianFix (U_64 * arg1, IDATA length, + U_64 arg2, U_32 * result); + IDATA lowestSetBit (U_64 * y); + IDATA timesTenToTheEHighPrecision (U_64 * result, IDATA length, jint e); + void simpleMultiplyAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2, + U_32 * result); + IDATA highestSetBit (U_64 * y); + IDATA lowestSetBitHighPrecision (U_64 * arg, IDATA length); + void simpleShiftLeftHighPrecision (U_64 * arg1, IDATA length, IDATA arg2); + UDATA floatMantissa (jfloat z); + U_64 simpleMultiplyHighPrecision64 (U_64 * arg1, IDATA length, U_64 arg2); + IDATA simpleAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2); + IDATA floatExponent (jfloat z); +#if defined(__cplusplus) +} +#endif +#endif /* cbigint_h */ diff --git a/luni/src/main/native/commonDblParce.c b/luni/src/main/native/commonDblParce.c new file mode 100644 index 0000000..5d85645 --- /dev/null +++ b/luni/src/main/native/commonDblParce.c @@ -0,0 +1,606 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <stdlib.h> +#include <math.h> + +#include "commonDblParce.h" +#include "cbigint.h" + + +/* ************************* Defines ************************* */ +#if defined(LINUX) || defined(FREEBSD) +#define USE_LL +#endif + +#define LOW_I32_FROM_VAR(u64) LOW_I32_FROM_LONG64(u64) +#define LOW_I32_FROM_PTR(u64ptr) LOW_I32_FROM_LONG64_PTR(u64ptr) +#define HIGH_I32_FROM_VAR(u64) HIGH_I32_FROM_LONG64(u64) +#define HIGH_I32_FROM_PTR(u64ptr) HIGH_I32_FROM_LONG64_PTR(u64ptr) + +#define MAX_ACCURACY_WIDTH 17 + +#define DEFAULT_WIDTH MAX_ACCURACY_WIDTH + +#if defined(USE_LL) +#define INFINITE_LONGBITS (0x7FF0000000000000LL) +#else +#if defined(USE_L) +#define INFINITE_LONGBITS (0x7FF0000000000000L) +#else +#define INFINITE_LONGBITS (0x7FF0000000000000) +#endif /* USE_L */ +#endif /* USE_LL */ + +#define MINIMUM_LONGBITS (0x1) + +#if defined(USE_LL) +#define MANTISSA_MASK (0x000FFFFFFFFFFFFFLL) +#define EXPONENT_MASK (0x7FF0000000000000LL) +#define NORMAL_MASK (0x0010000000000000LL) +#else +#if defined(USE_L) +#define MANTISSA_MASK (0x000FFFFFFFFFFFFFL) +#define EXPONENT_MASK (0x7FF0000000000000L) +#define NORMAL_MASK (0x0010000000000000L) +#else +#define MANTISSA_MASK (0x000FFFFFFFFFFFFF) +#define EXPONENT_MASK (0x7FF0000000000000) +#define NORMAL_MASK (0x0010000000000000) +#endif /* USE_L */ +#endif /* USE_LL */ + +#define DOUBLE_TO_LONGBITS(dbl) (*((U_64 *)(&dbl))) + +/* Keep a count of the number of times we decrement and increment to + * approximate the double, and attempt to detect the case where we + * could potentially toggle back and forth between decrementing and + * incrementing. It is possible for us to be stuck in the loop when + * incrementing by one or decrementing by one may exceed or stay below + * the value that we are looking for. In this case, just break out of + * the loop if we toggle between incrementing and decrementing for more + * than twice. + */ +#define INCREMENT_DOUBLE(_x, _decCount, _incCount) \ + { \ + ++DOUBLE_TO_LONGBITS(_x); \ + _incCount++; \ + if( (_incCount > 2) && (_decCount > 2) ) { \ + if( _decCount > _incCount ) { \ + DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \ + } else if( _incCount > _decCount ) { \ + DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \ + } \ + break; \ + } \ + } +#define DECREMENT_DOUBLE(_x, _decCount, _incCount) \ + { \ + --DOUBLE_TO_LONGBITS(_x); \ + _decCount++; \ + if( (_incCount > 2) && (_decCount > 2) ) { \ + if( _decCount > _incCount ) { \ + DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \ + } else if( _incCount > _decCount ) { \ + DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \ + } \ + break; \ + } \ + } + +#define allocateU64(x, n) if (!((x) = (U_64*) malloc((n) * sizeof(U_64)))) goto OutOfMemory; +#define release(r) if ((r)) free((r)); + +/* *********************************************************** */ + +/* ************************ local data ************************ */ +static const jdouble tens[] = { + 1.0, + 1.0e1, + 1.0e2, + 1.0e3, + 1.0e4, + 1.0e5, + 1.0e6, + 1.0e7, + 1.0e8, + 1.0e9, + 1.0e10, + 1.0e11, + 1.0e12, + 1.0e13, + 1.0e14, + 1.0e15, + 1.0e16, + 1.0e17, + 1.0e18, + 1.0e19, + 1.0e20, + 1.0e21, + 1.0e22 +}; +/* *********************************************************** */ + +/* ************** private function declarations ************** */ +static U_64 dblparse_shiftRight64 (U_64 * lp, volatile int mbe); + +static jdouble createDouble1 (JNIEnv * env, U_64 * f, IDATA length, jint e); +static jdouble doubleAlgorithm (JNIEnv * env, U_64 * f, IDATA length, jint e, + jdouble z); +/* *********************************************************** */ + +#define tenToTheE(e) (*(tens + (e))) +#define LOG5_OF_TWO_TO_THE_N 23 + +#define sizeOfTenToTheE(e) (((e) / 19) + 1) + +jdouble +createDouble (JNIEnv * env, const char *s, jint e) +{ + /* assumes s is a null terminated string with at least one + * character in it */ + U_64 def[DEFAULT_WIDTH]; + U_64 defBackup[DEFAULT_WIDTH]; + U_64 *f, *fNoOverflow, *g, *tempBackup; + U_32 overflow; + jdouble result; + IDATA index = 1; + int unprocessedDigits = 0; + + f = def; + fNoOverflow = defBackup; + *f = 0; + tempBackup = g = 0; + do + { + if (*s >= '0' && *s <= '9') + { + /* Make a back up of f before appending, so that we can + * back out of it if there is no more room, i.e. index > + * MAX_ACCURACY_WIDTH. + */ + memcpy (fNoOverflow, f, sizeof (U_64) * index); + overflow = + simpleAppendDecimalDigitHighPrecision (f, index, *s - '0'); + if (overflow) + { + f[index++] = overflow; + /* There is an overflow, but there is no more room + * to store the result. We really only need the top 52 + * bits anyway, so we must back out of the overflow, + * and ignore the rest of the string. + */ + if (index >= MAX_ACCURACY_WIDTH) + { + index--; + memcpy (f, fNoOverflow, sizeof (U_64) * index); + break; + } + if (tempBackup) + { + fNoOverflow = tempBackup; + } + } + } + else + index = -1; + } + while (index > 0 && *(++s) != '\0'); + + /* We've broken out of the parse loop either because we've reached + * the end of the string or we've overflowed the maximum accuracy + * limit of a double. If we still have unprocessed digits in the + * given string, then there are three possible results: + * 1. (unprocessed digits + e) == 0, in which case we simply + * convert the existing bits that are already parsed + * 2. (unprocessed digits + e) < 0, in which case we simply + * convert the existing bits that are already parsed along + * with the given e + * 3. (unprocessed digits + e) > 0 indicates that the value is + * simply too big to be stored as a double, so return Infinity + */ + if ((unprocessedDigits = strlen (s)) > 0) + { + e += unprocessedDigits; + if (index > -1) + { + if (e == 0) + result = toDoubleHighPrecision (f, index); + else if (e < 0) + result = createDouble1 (env, f, index, e); + else + { + DOUBLE_TO_LONGBITS (result) = INFINITE_LONGBITS; + } + } + else + { + LOW_I32_FROM_VAR (result) = -1; + HIGH_I32_FROM_VAR (result) = -1; + } + } + else + { + if (index > -1) + { + if (e == 0) + result = toDoubleHighPrecision (f, index); + else + result = createDouble1 (env, f, index, e); + } + else + { + LOW_I32_FROM_VAR (result) = -1; + HIGH_I32_FROM_VAR (result) = -1; + } + } + + return result; + +} + +jdouble +createDouble1 (JNIEnv * env, U_64 * f, IDATA length, jint e) +{ + IDATA numBits; + jdouble result; + +#define APPROX_MIN_MAGNITUDE -309 + +#define APPROX_MAX_MAGNITUDE 309 + + numBits = highestSetBitHighPrecision (f, length) + 1; + numBits -= lowestSetBitHighPrecision (f, length); + if (numBits < 54 && e >= 0 && e < LOG5_OF_TWO_TO_THE_N) + { + return toDoubleHighPrecision (f, length) * tenToTheE (e); + } + else if (numBits < 54 && e < 0 && (-e) < LOG5_OF_TWO_TO_THE_N) + { + return toDoubleHighPrecision (f, length) / tenToTheE (-e); + } + else if (e >= 0 && e < APPROX_MAX_MAGNITUDE) + { + result = toDoubleHighPrecision (f, length) * pow (10.0, e); + } + else if (e >= APPROX_MAX_MAGNITUDE) + { + /* Convert the partial result to make sure that the + * non-exponential part is not zero. This check fixes the case + * where the user enters 0.0e309! */ + result = toDoubleHighPrecision (f, length); + /* Don't go straight to zero as the fact that x*0 = 0 independent of x might + cause the algorithm to produce an incorrect result. Instead try the min value + first and let it fall to zero if need be. */ + + if (result == 0.0) + { + DOUBLE_TO_LONGBITS (result) = MINIMUM_LONGBITS; + } + else + { + DOUBLE_TO_LONGBITS (result) = INFINITE_LONGBITS; + return result; + } + } + else if (e > APPROX_MIN_MAGNITUDE) + { + result = toDoubleHighPrecision (f, length) / pow (10.0, -e); + } + + if (e <= APPROX_MIN_MAGNITUDE) + { + + result = toDoubleHighPrecision (f, length) * pow (10.0, e + 52); + result = result * pow (10.0, -52); + + } + + /* Don't go straight to zero as the fact that x*0 = 0 independent of x might + cause the algorithm to produce an incorrect result. Instead try the min value + first and let it fall to zero if need be. */ + + if (result == 0.0) + + DOUBLE_TO_LONGBITS (result) = MINIMUM_LONGBITS; + + return doubleAlgorithm (env, f, length, e, result); +} + +static U_64 +dblparse_shiftRight64 (U_64 * lp, volatile int mbe) +{ + U_64 b1Value = 0; + U_32 hi = HIGH_U32_FROM_LONG64_PTR (lp); + U_32 lo = LOW_U32_FROM_LONG64_PTR (lp); + int srAmt; + + if (mbe == 0) + return 0; + if (mbe >= 128) + { + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + return 0; + } + + /* Certain platforms do not handle de-referencing a 64-bit value + * from a pointer on the stack correctly (e.g. MVL-hh/XScale) + * because the pointer may not be properly aligned, so we'll have + * to handle two 32-bit chunks. */ + if (mbe < 32) + { + LOW_U32_FROM_LONG64 (b1Value) = 0; + HIGH_U32_FROM_LONG64 (b1Value) = lo << (32 - mbe); + LOW_U32_FROM_LONG64_PTR (lp) = (hi << (32 - mbe)) | (lo >> mbe); + HIGH_U32_FROM_LONG64_PTR (lp) = hi >> mbe; + } + else if (mbe == 32) + { + LOW_U32_FROM_LONG64 (b1Value) = 0; + HIGH_U32_FROM_LONG64 (b1Value) = lo; + LOW_U32_FROM_LONG64_PTR (lp) = hi; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + } + else if (mbe < 64) + { + srAmt = mbe - 32; + LOW_U32_FROM_LONG64 (b1Value) = lo << (32 - srAmt); + HIGH_U32_FROM_LONG64 (b1Value) = (hi << (32 - srAmt)) | (lo >> srAmt); + LOW_U32_FROM_LONG64_PTR (lp) = hi >> srAmt; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + } + else if (mbe == 64) + { + LOW_U32_FROM_LONG64 (b1Value) = lo; + HIGH_U32_FROM_LONG64 (b1Value) = hi; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + } + else if (mbe < 96) + { + srAmt = mbe - 64; + b1Value = *lp; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64 (b1Value) >>= srAmt; + LOW_U32_FROM_LONG64 (b1Value) |= (hi << (32 - srAmt)); + HIGH_U32_FROM_LONG64 (b1Value) >>= srAmt; + } + else if (mbe == 96) + { + LOW_U32_FROM_LONG64 (b1Value) = hi; + HIGH_U32_FROM_LONG64 (b1Value) = 0; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + } + else + { + LOW_U32_FROM_LONG64 (b1Value) = hi >> (mbe - 96); + HIGH_U32_FROM_LONG64 (b1Value) = 0; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + } + + return b1Value; +} + +#if defined(WIN32) +/* disable global optimizations on the microsoft compiler for the + * doubleAlgorithm function otherwise it won't compile */ +#pragma optimize("g",off) +#endif + + +/* The algorithm for the function doubleAlgorithm() below can be found + * in: + * + * "How to Read Floating-Point Numbers Accurately", William D. + * Clinger, Proceedings of the ACM SIGPLAN '90 Conference on + * Programming Language Design and Implementation, June 20-22, + * 1990, pp. 92-101. + * + * There is a possibility that the function will end up in an endless + * loop if the given approximating floating-point number (a very small + * floating-point whose value is very close to zero) straddles between + * two approximating integer values. We modified the algorithm slightly + * to detect the case where it oscillates back and forth between + * incrementing and decrementing the floating-point approximation. It + * is currently set such that if the oscillation occurs more than twice + * then return the original approximation. + */ +static jdouble +doubleAlgorithm (JNIEnv * env, U_64 * f, IDATA length, jint e, jdouble z) +{ + U_64 m; + IDATA k, comparison, comparison2; + U_64 *x, *y, *D, *D2; + IDATA xLength, yLength, DLength, D2Length, decApproxCount, incApproxCount; + + x = y = D = D2 = 0; + xLength = yLength = DLength = D2Length = 0; + decApproxCount = incApproxCount = 0; + + do + { + m = doubleMantissa (z); + k = doubleExponent (z); + + if (x && x != f) + free(x); + + release (y); + release (D); + release (D2); + + if (e >= 0 && k >= 0) + { + xLength = sizeOfTenToTheE (e) + length; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + timesTenToTheEHighPrecision (x, xLength, e); + + yLength = (k >> 6) + 2; + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + simpleShiftLeftHighPrecision (y, yLength, k); + } + else if (e >= 0) + { + xLength = sizeOfTenToTheE (e) + length + ((-k) >> 6) + 1; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + timesTenToTheEHighPrecision (x, xLength, e); + simpleShiftLeftHighPrecision (x, xLength, -k); + + yLength = 1; + allocateU64 (y, 1); + *y = m; + } + else if (k >= 0) + { + xLength = length; + x = f; + + yLength = sizeOfTenToTheE (-e) + 2 + (k >> 6); + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + timesTenToTheEHighPrecision (y, yLength, -e); + simpleShiftLeftHighPrecision (y, yLength, k); + } + else + { + xLength = length + ((-k) >> 6) + 1; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + simpleShiftLeftHighPrecision (x, xLength, -k); + + yLength = sizeOfTenToTheE (-e) + 1; + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + timesTenToTheEHighPrecision (y, yLength, -e); + } + + comparison = compareHighPrecision (x, xLength, y, yLength); + if (comparison > 0) + { /* x > y */ + DLength = xLength; + allocateU64 (D, DLength); + memcpy (D, x, DLength * sizeof (U_64)); + subtractHighPrecision (D, DLength, y, yLength); + } + else if (comparison) + { /* y > x */ + DLength = yLength; + allocateU64 (D, DLength); + memcpy (D, y, DLength * sizeof (U_64)); + subtractHighPrecision (D, DLength, x, xLength); + } + else + { /* y == x */ + DLength = 1; + allocateU64 (D, 1); + *D = 0; + } + + D2Length = DLength + 1; + allocateU64 (D2, D2Length); + m <<= 1; + multiplyHighPrecision (D, DLength, &m, 1, D2, D2Length); + m >>= 1; + + comparison2 = compareHighPrecision (D2, D2Length, y, yLength); + if (comparison2 < 0) + { + if (comparison < 0 && m == NORMAL_MASK) + { + simpleShiftLeftHighPrecision (D2, D2Length, 1); + if (compareHighPrecision (D2, D2Length, y, yLength) > 0) + { + DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + } + else + { + break; + } + } + else + { + break; + } + } + else if (comparison2 == 0) + { + if ((LOW_U32_FROM_VAR (m) & 1) == 0) + { + if (comparison < 0 && m == NORMAL_MASK) + { + DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + } + else + { + break; + } + } + else if (comparison < 0) + { + DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + break; + } + else + { + INCREMENT_DOUBLE (z, decApproxCount, incApproxCount); + break; + } + } + else if (comparison < 0) + { + DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + } + else + { + if (DOUBLE_TO_LONGBITS (z) == INFINITE_LONGBITS) + break; + INCREMENT_DOUBLE (z, decApproxCount, incApproxCount); + } + } + while (1); + + if (x && x != f) + free(x); + release (y); + release (D); + release (D2); + return z; + +OutOfMemory: + if (x && x != f) + free(x); + release (y); + release (y); + release (D); + release (D2); + + DOUBLE_TO_LONGBITS (z) = -2; + + return z; +} diff --git a/luni/src/main/native/commonDblParce.h b/luni/src/main/native/commonDblParce.h new file mode 100644 index 0000000..d693910 --- /dev/null +++ b/luni/src/main/native/commonDblParce.h @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <JNIHelp.h> +/* Header for class org_apache_harmony_text_BidiWrapper */ + +#if !defined(_Included_commonDblParce) +#define _Included_commonDblFltParce +#if defined(__cplusplus) +extern "C" +{ +#endif + +jdouble createDouble(JNIEnv * env, const char *s, jint e); + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/luni/src/main/native/fltconst.h b/luni/src/main/native/fltconst.h new file mode 100644 index 0000000..03a97cd --- /dev/null +++ b/luni/src/main/native/fltconst.h @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(fltconst_h) +#define fltconst_h + +#include "hycomp.h" + +/* IEEE floats consist of: sign bit, exponent field, significand field + single: 31 = sign bit, 30..23 = exponent (8 bits), 22..0 = significand (23 bits) + double: 63 = sign bit, 62..52 = exponent (11 bits), 51..0 = significand (52 bits) + inf == (all exponent bits set) and (all mantissa bits clear) + nan == (all exponent bits set) and (at least one mantissa bit set) + finite == (at least one exponent bit clear) + zero == (all exponent bits clear) and (all mantissa bits clear) + denormal == (all exponent bits clear) and (at least one mantissa bit set) + positive == sign bit clear + negative == sign bit set +*/ +#define MAX_U32_DOUBLE (ESDOUBLE) (4294967296.0) /* 2^32 */ +#define MAX_U32_SINGLE (ESSINGLE) (4294967296.0) /* 2^32 */ +#define HY_POS_PI (ESDOUBLE) (3.141592653589793) + +#ifdef HY_LITTLE_ENDIAN +#ifdef HY_PLATFORM_DOUBLE_ORDER +#define DOUBLE_LO_OFFSET 0 +#define DOUBLE_HI_OFFSET 1 +#else +#define DOUBLE_LO_OFFSET 1 +#define DOUBLE_HI_OFFSET 0 +#endif +#define LONG_LO_OFFSET 0 +#define LONG_HI_OFFSET 1 +#else +#ifdef HY_PLATFORM_DOUBLE_ORDER +#define DOUBLE_LO_OFFSET 1 +#define DOUBLE_HI_OFFSET 0 +#else +#define DOUBLE_LO_OFFSET 0 +#define DOUBLE_HI_OFFSET 1 +#endif +#define LONG_LO_OFFSET 1 +#define LONG_HI_OFFSET 0 +#endif + +#define RETURN_FINITE 0 +#define RETURN_NAN 1 +#define RETURN_POS_INF 2 +#define RETURN_NEG_INF 3 +#define DOUBLE_SIGN_MASK_HI 0x80000000 +#define DOUBLE_EXPONENT_MASK_HI 0x7FF00000 +#define DOUBLE_MANTISSA_MASK_LO 0xFFFFFFFF +#define DOUBLE_MANTISSA_MASK_HI 0x000FFFFF +#define SINGLE_SIGN_MASK 0x80000000 +#define SINGLE_EXPONENT_MASK 0x7F800000 +#define SINGLE_MANTISSA_MASK 0x007FFFFF +#define SINGLE_NAN_BITS (SINGLE_EXPONENT_MASK | 0x00400000) + +typedef union u64u32dbl_tag { + U_64 u64val; + U_32 u32val[2]; + I_32 i32val[2]; + double dval; +} U64U32DBL; + +/* Replace P_FLOAT_HI and P_FLOAT_LOW */ +/* These macros are used to access the high and low 32-bit parts of a double (64-bit) value. */ +#define LOW_U32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->u32val[DOUBLE_LO_OFFSET]) +#define HIGH_U32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->u32val[DOUBLE_HI_OFFSET]) +#define LOW_I32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->i32val[DOUBLE_LO_OFFSET]) +#define HIGH_I32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->i32val[DOUBLE_HI_OFFSET]) +#define LOW_U32_FROM_DBL(dbl) LOW_U32_FROM_DBL_PTR(&(dbl)) +#define HIGH_U32_FROM_DBL(dbl) HIGH_U32_FROM_DBL_PTR(&(dbl)) +#define LOW_I32_FROM_DBL(dbl) LOW_I32_FROM_DBL_PTR(&(dbl)) +#define HIGH_I32_FROM_DBL(dbl) HIGH_I32_FROM_DBL_PTR(&(dbl)) +#define LOW_U32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->u32val[LONG_LO_OFFSET]) +#define HIGH_U32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->u32val[LONG_HI_OFFSET]) +#define LOW_I32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->i32val[LONG_LO_OFFSET]) +#define HIGH_I32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->i32val[LONG_HI_OFFSET]) +#define LOW_U32_FROM_LONG64(long64) LOW_U32_FROM_LONG64_PTR(&(long64)) +#define HIGH_U32_FROM_LONG64(long64) HIGH_U32_FROM_LONG64_PTR(&(long64)) +#define LOW_I32_FROM_LONG64(long64) LOW_I32_FROM_LONG64_PTR(&(long64)) +#define HIGH_I32_FROM_LONG64(long64) HIGH_I32_FROM_LONG64_PTR(&(long64)) +#define IS_ZERO_DBL_PTR(dblptr) ((LOW_U32_FROM_DBL_PTR(dblptr) == 0) && ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0) || (HIGH_U32_FROM_DBL_PTR(dblptr) == DOUBLE_SIGN_MASK_HI))) +#define IS_ONE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0x3ff00000 || HIGH_U32_FROM_DBL_PTR(dblptr) == 0xbff00000) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0)) +#define IS_NAN_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) == DOUBLE_EXPONENT_MASK_HI) && (LOW_U32_FROM_DBL_PTR(dblptr) | (HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_MANTISSA_MASK_HI))) +#define IS_INF_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & (DOUBLE_EXPONENT_MASK_HI|DOUBLE_MANTISSA_MASK_HI)) == DOUBLE_EXPONENT_MASK_HI) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0)) +#define IS_DENORMAL_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) == 0) && ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_MANTISSA_MASK_HI) != 0 || (LOW_U32_FROM_DBL_PTR(dblptr) != 0))) +#define IS_FINITE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) < DOUBLE_EXPONENT_MASK_HI) +#define IS_POSITIVE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_SIGN_MASK_HI) == 0) +#define IS_NEGATIVE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_SIGN_MASK_HI) != 0) +#define IS_NEGATIVE_MAX_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0xFFEFFFFF) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0xFFFFFFFF)) +#define IS_ZERO_DBL(dbl) IS_ZERO_DBL_PTR(&(dbl)) +#define IS_ONE_DBL(dbl) IS_ONE_DBL_PTR(&(dbl)) +#define IS_NAN_DBL(dbl) IS_NAN_DBL_PTR(&(dbl)) +#define IS_INF_DBL(dbl) IS_INF_DBL_PTR(&(dbl)) +#define IS_DENORMAL_DBL(dbl) IS_DENORMAL_DBL_PTR(&(dbl)) +#define IS_FINITE_DBL(dbl) IS_FINITE_DBL_PTR(&(dbl)) +#define IS_POSITIVE_DBL(dbl) IS_POSITIVE_DBL_PTR(&(dbl)) +#define IS_NEGATIVE_DBL(dbl) IS_NEGATIVE_DBL_PTR(&(dbl)) +#define IS_NEGATIVE_MAX_DBL(dbl) IS_NEGATIVE_MAX_DBL_PTR(&(dbl)) +#define IS_ZERO_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) == (U_32)0) +#define IS_ONE_SNGL_PTR(fltptr) ((*U32P((fltptr)) == 0x3f800000) || (*U32P((fltptr)) == 0xbf800000)) +#define IS_NAN_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) > (U_32)SINGLE_EXPONENT_MASK) +#define IS_INF_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) == (U_32)SINGLE_EXPONENT_MASK) +#define IS_DENORMAL_SNGL_PTR(fltptr) (((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK)-(U_32)1) < (U_32)SINGLE_MANTISSA_MASK) +#define IS_FINITE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) < (U_32)SINGLE_EXPONENT_MASK) +#define IS_POSITIVE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)SINGLE_SIGN_MASK) == (U_32)0) +#define IS_NEGATIVE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)SINGLE_SIGN_MASK) != (U_32)0) +#define IS_ZERO_SNGL(flt) IS_ZERO_SNGL_PTR(&(flt)) +#define IS_ONE_SNGL(flt) IS_ONE_SNGL_PTR(&(flt)) +#define IS_NAN_SNGL(flt) IS_NAN_SNGL_PTR(&(flt)) +#define IS_INF_SNGL(flt) IS_INF_SNGL_PTR(&(flt)) +#define IS_DENORMAL_SNGL(flt) IS_DENORMAL_SNGL_PTR(&(flt)) +#define IS_FINITE_SNGL(flt) IS_FINITE_SNGL_PTR(&(flt)) +#define IS_POSITIVE_SNGL(flt) IS_POSITIVE_SNGL_PTR(&(flt)) +#define IS_NEGATIVE_SNGL(flt) IS_NEGATIVE_SNGL_PTR(&(flt)) +#define SET_NAN_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = (DOUBLE_EXPONENT_MASK_HI | 0x00080000); LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_PZERO_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = 0; LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_NZERO_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = DOUBLE_SIGN_MASK_HI; LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_PINF_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = DOUBLE_EXPONENT_MASK_HI; LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_NINF_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = (DOUBLE_EXPONENT_MASK_HI | DOUBLE_SIGN_MASK_HI); LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_NAN_SNGL_PTR(fltptr) *U32P((fltptr)) = ((U_32)SINGLE_NAN_BITS) +#define SET_PZERO_SNGL_PTR(fltptr) *U32P((fltptr)) = 0 +#define SET_NZERO_SNGL_PTR(fltptr) *U32P((fltptr)) = SINGLE_SIGN_MASK +#define SET_PINF_SNGL_PTR(fltptr) *U32P((fltptr)) = SINGLE_EXPONENT_MASK +#define SET_NINF_SNGL_PTR(fltptr) *U32P((fltptr)) = (SINGLE_EXPONENT_MASK | SINGLE_SIGN_MASK) + +/* on some platforms (HP720) we cannot reference an unaligned float. Build them by hand, one U_32 at a time. */ +#if defined(ATOMIC_FLOAT_ACCESS) +#define PTR_DOUBLE_STORE(dstPtr, aDoublePtr) HIGH_U32_FROM_DBL_PTR(dstPtr) = HIGH_U32_FROM_DBL_PTR(aDoublePtr); LOW_U32_FROM_DBL_PTR(dstPtr) = LOW_U32_FROM_DBL_PTR(aDoublePtr) +#define PTR_DOUBLE_VALUE(dstPtr, aDoublePtr) HIGH_U32_FROM_DBL_PTR(aDoublePtr) = HIGH_U32_FROM_DBL_PTR(dstPtr); LOW_U32_FROM_DBL_PTR(aDoublePtr) = LOW_U32_FROM_DBL_PTR(dstPtr) +#else +#define PTR_DOUBLE_STORE(dstPtr, aDoublePtr) (*(dstPtr) = *(aDoublePtr)) +#define PTR_DOUBLE_VALUE(dstPtr, aDoublePtr) (*(aDoublePtr) = *(dstPtr)) +#endif + +#define STORE_LONG(dstPtr, hi, lo) HIGH_U32_FROM_LONG64_PTR(dstPtr) = (hi); LOW_U32_FROM_LONG64_PTR(dstPtr) = (lo) +#define PTR_SINGLE_VALUE(dstPtr, aSinglePtr) (*U32P(aSinglePtr) = *U32P(dstPtr)) +#define PTR_SINGLE_STORE(dstPtr, aSinglePtr) *((U_32 *)(dstPtr)) = (*U32P(aSinglePtr)) + +#endif /* fltconst_h */ diff --git a/luni/src/main/native/hycomp.h b/luni/src/main/native/hycomp.h new file mode 100644 index 0000000..cd8ce73 --- /dev/null +++ b/luni/src/main/native/hycomp.h @@ -0,0 +1,452 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(hycomp_h) +#define hycomp_h + +#if !defined(LINUX) +#define LINUX 1 +#endif + +/** + * USE_PROTOTYPES: Use full ANSI prototypes. + * + * CLOCK_PRIMS: We want the timer/clock prims to be used + * + * LITTLE_ENDIAN: This is for the intel machines or other + * little endian processors. Defaults to big endian. + * + * NO_LVALUE_CASTING: This is for compilers that don't like the left side + * of assigns to be cast. It hacks around to do the + * right thing. + * + * ATOMIC_FLOAT_ACCESS: So that float operations will work. + * + * LINKED_USER_PRIMITIVES: Indicates that user primitives are statically linked + * with the VM executeable. + * + * OLD_SPACE_SIZE_DIFF: The 68k uses a different amount of old space. + * This "legitimizes" the change. + * + * SIMPLE_SIGNAL: For machines that don't use real signals in C. + * (eg: PC, 68k) + * + * OS_NAME_LOOKUP: Use nlist to lookup user primitive addresses. + * + * VMCALL: Tag for all functions called by the VM. + * + * VMAPICALL: Tag for all functions called via the PlatformFunction + * callWith: mechanism. + * + * SYS_FLOAT: For some math functions where extended types (80 or 96 bits) are returned + * Most platforms return as a double + * + * FLOAT_EXTENDED: If defined, the type name for extended precision floats. + * + * PLATFORM_IS_ASCII: Must be defined if the platform is ASCII + * + * EXE_EXTENSION_CHAR: the executable has a delimiter that we want to stop at as part of argv[0]. + */ + + /** + * By default order doubles in the native (that is big/little endian) ordering. + */ + +#define HY_PLATFORM_DOUBLE_ORDER + +/** + * Define common types: + * <ul> + * <li><code>U_32 / I_32</code> - unsigned/signed 32 bits</li> + * <li><code>U_16 / I_16</code> - unsigned/signed 16 bits</li> + * <li><code>U_8 / I_8</code> - unsigned/signed 8 bits (bytes -- not to be + * confused with char)</li> + * </ul> + */ + +typedef int I_32; +typedef short I_16; +typedef signed char I_8; /* chars can be unsigned */ +typedef unsigned int U_32; +typedef unsigned short U_16; +typedef unsigned char U_8; + +/** + * Define platform specific types: + * <ul> + * <li><code>U_64 / I_64</code> - unsigned/signed 64 bits</li> + * </ul> + */ + +#if defined(LINUX) || defined(FREEBSD) || defined(AIX) + +#define DATA_TYPES_DEFINED + +/* NOTE: Linux supports different processors -- do not assume 386 */ + #if defined(HYX86_64) || defined(HYIA64) || defined(HYPPC64) || defined(HYS390X) + + typedef unsigned long int U_64; /* 64bits */ + typedef long int I_64; + #define TOC_UNWRAP_ADDRESS(wrappedPointer) ((void *) (wrappedPointer)[0]) + #define TOC_STORE_TOC(dest,wrappedPointer) (dest = ((UDATA*)wrappedPointer)[1]) + + #define HY_WORD64 + + #else + + typedef unsigned long long U_64; + typedef long long I_64; + + #endif + + #if defined(HYS390X) || defined(HYS390) || defined(HYPPC64) || defined(HYPPC32) + #define HY_BIG_ENDIAN + #else + #define HY_LITTLE_ENDIAN + #endif + + #if defined(HYPPC32) + #define VA_PTR(valist) (&valist[0]) + #endif + + typedef double SYS_FLOAT; + #define HYCONST64(x) x##LL + #define NO_LVALUE_CASTING + #define FLOAT_EXTENDED long double + #define PLATFORM_IS_ASCII + #define PLATFORM_LINE_DELIMITER "\012" + #define DIR_SEPARATOR '/' + #define DIR_SEPARATOR_STR "/" + +/** + * No priorities on Linux + */ + + #define HY_PRIORITY_MAP {0,0,0,0,0,0,0,0,0,0,0,0} + + typedef U_32 BOOLEAN; + +#endif + +/* Win32 - Windows 3.1 & NT using Win32 */ +#if defined(WIN32) + + #define HY_LITTLE_ENDIAN + +/* Define 64-bit integers for Windows */ + typedef __int64 I_64; + typedef unsigned __int64 U_64; + + typedef double SYS_FLOAT; + #define NO_LVALUE_CASTING + #define VMAPICALL _stdcall + #define VMCALL _cdecl + #define EXE_EXTENSION_CHAR '.' + + #define DIR_SEPARATOR '\\' + #define DIR_SEPARATOR_STR "\\" + +/* Modifications for the Alpha running WIN-NT */ + #if defined(_ALPHA_) + #undef small /* defined as char in rpcndr.h */ + typedef double FLOAT_EXTENDED; + #endif + + #define HY_PRIORITY_MAP { \ + THREAD_PRIORITY_IDLE, /* 0 */\ + THREAD_PRIORITY_LOWEST, /* 1 */\ + THREAD_PRIORITY_BELOW_NORMAL, /* 2 */\ + THREAD_PRIORITY_BELOW_NORMAL, /* 3 */\ + THREAD_PRIORITY_BELOW_NORMAL, /* 4 */\ + THREAD_PRIORITY_NORMAL, /* 5 */\ + THREAD_PRIORITY_ABOVE_NORMAL, /* 6 */\ + THREAD_PRIORITY_ABOVE_NORMAL, /* 7 */\ + THREAD_PRIORITY_ABOVE_NORMAL, /* 8 */\ + THREAD_PRIORITY_ABOVE_NORMAL, /* 9 */\ + THREAD_PRIORITY_HIGHEST, /*10 */\ + THREAD_PRIORITY_TIME_CRITICAL /*11 */} + +#endif /* defined(WIN32) */ + +#if !defined(VMCALL) + #define VMCALL + #define VMAPICALL +#endif +#define PVMCALL VMCALL * + +#define GLOBAL_DATA(symbol) ((void*)&(symbol)) +#define GLOBAL_TABLE(symbol) GLOBAL_DATA(symbol) + +/** + * Define platform specific types: + * <ul> + * <li><code>UDATA</code> - unsigned data, can be used as an integer or + * pointer storage</li> + * <li><code>IDATA</code> - signed data, can be used as an integer or + * pointer storage</li> + * </ul> + */ +/* FIXME: POINTER64 */ +#if defined(HYX86_64) || defined(HYIA64) || defined(HYPPC64) || defined(HYS390X) || defined(POINTER64) + +typedef I_64 IDATA; +typedef U_64 UDATA; + +#else /* this is default for non-64bit systems */ + +typedef I_32 IDATA; +typedef U_32 UDATA; + +#endif /* defined(HYX86_64) */ + +#if !defined(DATA_TYPES_DEFINED) +/* no generic U_64 or I_64 */ + +/* don't typedef BOOLEAN since it's already def'ed on Win32 */ +#define BOOLEAN UDATA + +#ifndef HY_BIG_ENDIAN +#define HY_LITTLE_ENDIAN +#endif + +#endif + +#if !defined(HYCONST64) +#define HYCONST64(x) x##L +#endif + +#if !defined(HY_DEFAULT_SCHED) + +/** + * By default, pthreads platforms use the <code>SCHED_OTHER</code> thread + * scheduling policy. + */ + +#define HY_DEFAULT_SCHED SCHED_OTHER +#endif + +#if !defined(HY_PRIORITY_MAP) + +/** + * If no priority map if provided, priorities will be determined + * algorithmically. + */ + +#endif + +#if !defined(FALSE) +#define FALSE ((BOOLEAN) 0) +#if !defined(TRUE) +#define TRUE ((BOOLEAN) (!FALSE)) +#endif +#endif + +#if !defined(NULL) +#if defined(__cplusplus) +#define NULL (0) +#else +#define NULL ((void *)0) +#endif +#endif +#define USE_PROTOTYPES +#if defined(USE_PROTOTYPES) +#define PROTOTYPE(x) x +#define VARARGS , ... +#else +#define PROTOTYPE(x) () +#define VARARGS +#endif + +/** + * Assign the default line delimiter, if it was not set. + */ + +#if !defined(PLATFORM_LINE_DELIMITER) +#define PLATFORM_LINE_DELIMITER "\015\012" +#endif + +/** + * Set the max path length, if it was not set. + */ + +#if !defined(MAX_IMAGE_PATH_LENGTH) +#define MAX_IMAGE_PATH_LENGTH (2048) +#endif +typedef double ESDOUBLE; +typedef float ESSINGLE; + +/** + * Helpers for U_64s. + */ + +#define CLEAR_U64(u64) (u64 = (U_64)0) +#define LOW_LONG(l) (*((U_32 *) &(l))) +#define HIGH_LONG(l) (*(((U_32 *) &(l)) + 1)) +#define I8(x) ((I_8) (x)) +#define I8P(x) ((I_8 *) (x)) +#define U16(x) ((U_16) (x)) +#define I16(x) ((I_16) (x)) +#define I16P(x) ((I_16 *) (x)) +#define U32(x) ((U_32) (x)) +#define I32(x) ((I_32) (x)) +#define I32P(x) ((I_32 *) (x)) +#define U16P(x) ((U_16 *) (x)) +#define U32P(x) ((U_32 *) (x)) +#define OBJP(x) ((HyObject *) (x)) +#define OBJPP(x) ((HyObject **) (x)) +#define OBJPPP(x) ((HyObject ***) (x)) +#define CLASSP(x) ((Class *) (x)) +#define CLASSPP(x) ((Class **) (x)) +#define BYTEP(x) ((BYTE *) (x)) + +/** + * Test - was conflicting with OS2.h + */ + +#define ESCHAR(x) ((CHARACTER) (x)) +#define FLT(x) ((FLOAT) x) +#define FLTP(x) ((FLOAT *) (x)) +#if defined(NO_LVALUE_CASTING) +#define LI8(x) (*((I_8 *) &(x))) +#define LI8P(x) (*((I_8 **) &(x))) +#define LU16(x) (*((U_16 *) &(x))) +#define LI16(x) (*((I_16 *) &(x))) +#define LU32(x) (*((U_32 *) &(x))) +#define LI32(x) (*((I_32 *) &(x))) +#define LI32P(x) (*((I_32 **) &(x))) +#define LU16P(x) (*((U_16 **) &(x))) +#define LU32P(x) (*((U_32 **) &(x))) +#define LOBJP(x) (*((HyObject **) &(x))) +#define LOBJPP(x) (*((HyObject ***) &(x))) +#define LOBJPPP(x) (*((HyObject ****) &(x)) +#define LCLASSP(x) (*((Class **) &(x))) +#define LBYTEP(x) (*((BYTE **) &(x))) +#define LCHAR(x) (*((CHARACTER) &(x))) +#define LFLT(x) (*((FLOAT) &x)) +#define LFLTP(x) (*((FLOAT *) &(x))) +#else +#define LI8(x) I8((x)) +#define LI8P(x) I8P((x)) +#define LU16(x) U16((x)) +#define LI16(x) I16((x)) +#define LU32(x) U32((x)) +#define LI32(x) I32((x)) +#define LI32P(x) I32P((x)) +#define LU16P(x) U16P((x)) +#define LU32P(x) U32P((x)) +#define LOBJP(x) OBJP((x)) +#define LOBJPP(x) OBJPP((x)) +#define LOBJPPP(x) OBJPPP((x)) +#define LIOBJP(x) IOBJP((x)) +#define LCLASSP(x) CLASSP((x)) +#define LBYTEP(x) BYTEP((x)) +#define LCHAR(x) CHAR((x)) +#define LFLT(x) FLT((x)) +#define LFLTP(x) FLTP((x)) +#endif + +/** + * Macros for converting between words and longs and accessing bits. + */ + +#define HIGH_WORD(x) U16(U32((x)) >> 16) +#define LOW_WORD(x) U16(U32((x)) & 0xFFFF) +#define LOW_BIT(o) (U32((o)) & 1) +#define LOW_2_BITS(o) (U32((o)) & 3) +#define LOW_3_BITS(o) (U32((o)) & 7) +#define LOW_4_BITS(o) (U32((o)) & 15) +#define MAKE_32(h, l) ((U32((h)) << 16) | U32((l))) +#define MAKE_64(h, l) ((((I_64)(h)) << 32) | (l)) +#if defined(__cplusplus) +#define HY_CFUNC "C" +#define HY_CDATA "C" +#else +#define HY_CFUNC +#define HY_CDATA +#endif + +/** + * Macros for tagging functions which read/write the vm thread. + */ + +#define READSVMTHREAD +#define WRITESVMTHREAD +#define REQUIRESSTACKFRAME + +/** + * Macro for tagging functions, which never return. + */ + +#if defined(__GNUC__) + +/** + * On GCC, we can actually pass this information on to the compiler. + */ + +#define NORETURN __attribute__((noreturn)) +#else +#define NORETURN +#endif + +/** + * On some systems va_list is an array type. This is probably in + * violation of the ANSI C spec, but it's not entirely clear. Because of + * this, we end up with an undesired extra level of indirection if we take + * the address of a va_list argument. + * + * To get it right, always use the VA_PTR macro + */ + +#if !defined(VA_PTR) +#define VA_PTR(valist) (&valist) +#endif +#if !defined(TOC_UNWRAP_ADDRESS) +#define TOC_UNWRAP_ADDRESS(wrappedPointer) (wrappedPointer) +#endif + +#if !defined(TOC_STORE_TOC) +#define TOC_STORE_TOC(dest,wrappedPointer) +#endif +/** + * Macros for accessing I_64 values. + */ + +#if defined(ATOMIC_LONG_ACCESS) +#define PTR_LONG_STORE(dstPtr, aLongPtr) ((*U32P(dstPtr) = *U32P(aLongPtr)), (*(U32P(dstPtr)+1) = *(U32P(aLongPtr)+1))) +#define PTR_LONG_VALUE(dstPtr, aLongPtr) ((*U32P(aLongPtr) = *U32P(dstPtr)), (*(U32P(aLongPtr)+1) = *(U32P(dstPtr)+1))) +#else +#define PTR_LONG_STORE(dstPtr, aLongPtr) (*(dstPtr) = *(aLongPtr)) +#define PTR_LONG_VALUE(dstPtr, aLongPtr) (*(aLongPtr) = *(dstPtr)) +#endif + +/** + * Macro used when declaring tables which require relocations. + */ + +#if !defined(HYCONST_TABLE) +#define HYCONST_TABLE const +#endif + +/** + * ANSI qsort is not always available. + */ + +#if !defined(HY_SORT) +#define HY_SORT(base, nmemb, size, compare) qsort((base), (nmemb), (size), (compare)) +#endif + +#endif /* hycomp_h */ diff --git a/luni/src/main/native/java_io_File.c b/luni/src/main/native/java_io_File.c new file mode 100644 index 0000000..bf89681 --- /dev/null +++ b/luni/src/main/native/java_io_File.c @@ -0,0 +1,704 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define MaxPath 1024 + +#include "JNIHelp.h" + +#include <string.h> +#include <fcntl.h> +#include <time.h> +#include <utime.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> +#include <assert.h> + + +/* these were copied from java.io.File */ +enum { + STAT_TYPE_EXISTS = 0x0001, + STAT_TYPE_DIR = 0x0002, + STAT_TYPE_FILE = 0x0004 +}; + +static void convertToPlatform(char *path) { + char *pathIndex; + + pathIndex = path; + while(*pathIndex != '\0') { + if(*pathIndex == '\\') { + *pathIndex = '/'; + } + pathIndex++; + } +} + +/* + * private static native byte[][] rootsImpl() + * + * Returns the linux root in an array of byte arrays + */ +static jobject java_io_File_rootsImpl(JNIEnv* env, jclass clazz) { + char rootStrings[3]; + jarray answer; + + rootStrings[0] = '/'; + rootStrings[1] = '\0'; + rootStrings[2] = '\0'; + + jclass arrayClass = (*env)->FindClass(env, "[B"); + if (arrayClass == NULL) + return NULL; + + answer = (*env)->NewObjectArray(env, 1, arrayClass, NULL); + if (!answer) + return NULL; + + jbyteArray rootname; + + rootname = (*env)->NewByteArray(env, 3); + (*env)->SetByteArrayRegion(env, rootname, 0, 3, (jbyte *) rootStrings); + (*env)->SetObjectArrayElement(env, answer, 0, rootname); + //(*env)->DeleteLocalRef(env, rootname); + + return answer; +} + +static jbyteArray java_io_File_getCanonImpl(JNIEnv * env, jobject recv, + jbyteArray path) { + /* This needs work. Currently it does no more or less than VAJ-20 ST + * implementationbut really should figure out '..', '.', and really + * resolve references. + */ + jbyteArray answer; + size_t answerlen; + char *pathIndex; + char pathCopy[MaxPath]; + jsize length = (jsize) (*env)->GetArrayLength(env, path); + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); + pathCopy[length] = '\0'; + + convertToPlatform(pathCopy); + + answerlen = strlen(pathCopy); + answer = (*env)->NewByteArray(env, answerlen); + (*env)->SetByteArrayRegion(env, answer, 0, answerlen, (jbyte *) pathCopy); + + return answer; +} + +/* + * native private boolean deleteFileImpl() + * + * Returns "true" if the file exists and was successfully deleted. + */ +static jboolean java_io_File_deleteFileImpl(JNIEnv* env, jobject obj, + jbyteArray path) { + + int cc; + + if(path == NULL) { + return JNI_FALSE; /* exception thrown */ + } + + char pathCopy[MaxPath]; + jsize length = (jsize) (*env)->GetArrayLength(env, path); + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + cc = unlink(pathCopy); + if(cc < 0) { + int err = errno; + + /* + * According to the man pages, Linux uses EISDIR and Mac OS X + * uses EPERM to indicate a non-super-user attempt to unlink + * a directory. Mac OS does have EISDIR in the header file. + * + * We should get EACCES if the problem is directory permissions. + */ + if(err == EISDIR || err == EPERM) { + cc = rmdir(pathCopy); + if(cc < 0) { + /* probably ENOTEMPTY */ + LOGD("unable to rmdir '%s': %s (errno=%d)\n", + pathCopy, strerror(err), err); + } + } else { + LOGD("unable to unlink '%s': %s (errno=%d)\n", + pathCopy, strerror(err), err); + } + } + + return (cc == 0); +} + +/* + * harmony implements this method practically identical to the deleteFileImpl, + * except that it uses a diffrent helper method from hyport. Dalvik seems to + * just need this one method to delete both + */ +static jboolean java_io_File_deleteDirImpl(JNIEnv * env, jobject recv, + jbyteArray path) { + return java_io_File_deleteFileImpl( env, recv, path); +} + +/* + * native public long lengthImpl() + * + * Returns the file length, or 0 if the file does not exist. The result for + * a directory is not defined. + */ +static jlong java_io_File_lengthImpl(JNIEnv* env, jobject obj, + jbyteArray path) { + struct stat sb; + jlong result = 0; + int cc; + + char pathCopy[MaxPath]; + jsize length = (jsize) (*env)->GetArrayLength(env, path); + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + cc = stat(pathCopy, &sb); + if(cc == 0) { + // BEGIN android-added + /* + * This explicitly treats non-regular files (e.g., sockets and + * block-special devices) as having size zero. Some synthetic + * "regular" files may report an arbitrary non-zero size, but + * in these cases they generally report a block count of zero. + * So, use a zero block count to trump any other concept of + * size. + */ + if (S_ISREG(sb.st_mode) && (sb.st_blocks != 0)) { + result = sb.st_size; + } else { + result = 0; + } + // END android-added + // BEGIN android-deleted + //result = sb.st_size; + // END android-deleted + } + + return result; +} + +/* + * native public long lastModified() + * + * Get the last modified date of the file. Measured in milliseconds + * from epoch (00:00:00 GMT, January 1, 1970). Returns 0 if the file does + * not exist + */ +static jlong java_io_File_lastModifiedImpl(JNIEnv* env, jobject obj, + jbyteArray path) { + struct stat sb; + jlong result = 0; + int cc; + + char pathCopy[MaxPath]; + jsize length = (jsize) (*env)->GetArrayLength(env, path); + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + cc = stat(pathCopy, &sb); + if(cc == 0) { + // sb.st_mtime is a time_t which is in seconds since epoch. + result = sb.st_mtime; + result *= 1000L; + } + + return result; +} + +/* + * private static native int stattype(String path) + */ +static jint java_io_File_stattype(JNIEnv* env, jobject recv, + jbyteArray pathStr) { + + char pathCopy[MaxPath]; + jsize length = (jsize) (*env)->GetArrayLength(env, pathStr); + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)pathCopy); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + struct stat sb; + int cc, type; + + type = 0; + cc = stat(pathCopy, &sb); + + if(cc == 0) { + type |= STAT_TYPE_EXISTS; + if(S_ISDIR(sb.st_mode)) { + type |= STAT_TYPE_DIR; + } else if(S_ISREG(sb.st_mode)) { + type |= STAT_TYPE_FILE; + } + } + + return type; +} + +static jboolean java_io_File_isDirectoryImpl(JNIEnv* env, jobject recv, + jbyteArray pathStr) { + return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_DIR) + == STAT_TYPE_DIR); +} + +static jboolean java_io_File_existsImpl(JNIEnv* env, jobject recv, + jbyteArray pathStr) { + return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_EXISTS) + == STAT_TYPE_EXISTS); +} + +static jboolean java_io_File_isFileImpl(JNIEnv* env, jobject recv, + jbyteArray pathStr) { + return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_FILE) + == STAT_TYPE_FILE); +} + +static jboolean java_io_File_isHiddenImpl(JNIEnv * env, jobject recv, + jbyteArray path) { + + char pathCopy[MaxPath]; + jsize index; + jsize length = (*env)->GetArrayLength(env, path); + length = length < MaxPath - 1 ? length : MaxPath - 1; + + if(length == 0) { + return 0; + } + + ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + if(!java_io_File_existsImpl(env, recv, path)) { + return 0; + } + + for(index = length; index >= 0; index--) { + if(pathCopy[index] == '.' + && (index > 0 && pathCopy[index - 1] == '/')) { + return -1; + } + } + + return 0; +} + +static jboolean java_io_File_readable(JNIEnv* env, jobject recv, + jbyteArray pathStr) { + char path[MaxPath]; + struct stat sb; + int cc, type; + + if(pathStr == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return -1; + } + + jsize length = (jsize) (*env)->GetArrayLength(env, pathStr); + + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)path); + path[length] = '\0'; + convertToPlatform(path); + + cc = access(path, R_OK); + + return cc == 0; +} + +static jboolean java_io_File_writable(JNIEnv* env, jobject recv, + jbyteArray pathStr) { + char path[MaxPath]; + struct stat sb; + int cc, type; + + if(pathStr == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return -1; + } + + jsize length = (jsize) (*env)->GetArrayLength(env, pathStr); + + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)path); + path[length] = '\0'; + convertToPlatform(path); + + cc = access(path, W_OK); + + return cc == 0; +} + +// BEGIN android-deleted +#if 0 +static jboolean java_io_File_isReadOnlyImpl(JNIEnv* env, jobject recv, + jbyteArray path) { + + return (java_io_File_readable(env, recv, path) + && !java_io_File_writable(env, recv, path)); +} + +static jboolean java_io_File_isWriteOnlyImpl(JNIEnv* env, jobject recv, + jbyteArray path) { + + return (!java_io_File_readable(env, recv, path) + && java_io_File_writable(env, recv, path)); +} +#endif +// END android-deleted + +static jbyteArray java_io_File_getLinkImpl(JNIEnv* env, jobject recv, + jbyteArray path) { + jbyteArray answer; + jsize answerlen; + char pathCopy[MaxPath]; + + jsize length = (jsize) (*env)->GetArrayLength(env, path); + + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + jboolean test = -1; + + char *link = pathCopy; + + int size = readlink(link, link, MaxPath); + if(size <= 0) { + test = 0; + } else { + if(size >= MaxPath) { + link[MaxPath - 1] = 0; + } else { + link[size] = 0; + } + } + + if(test) { + answerlen = strlen(pathCopy); + answer = (*env)->NewByteArray(env, answerlen); + (*env)->SetByteArrayRegion(env, answer, 0, answerlen, + (jbyte *) pathCopy); + } else { + answer = path; + } + + return answer; +} + +static jboolean java_io_File_setLastModifiedImpl(JNIEnv* env, jobject recv, + jbyteArray path, jlong time) { + jboolean result; + + jsize length = (*env)->GetArrayLength(env, path); + char pathCopy[MaxPath]; + length = length < MaxPath - 1 ? length : MaxPath - 1; + ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + struct stat statbuf; + struct utimbuf timebuf; + if(stat(pathCopy, &statbuf)) { + result = 0; + } else { + timebuf.actime = statbuf.st_atime; + timebuf.modtime = (time_t) (time / 1000); + result = utime(pathCopy, &timebuf) == 0; + } + + return result; +} + +static jboolean java_io_File_setReadOnlyImpl(JNIEnv* env, jobject recv, + jbyteArray path) { + jsize length = (*env)->GetArrayLength(env, path); + char pathCopy[MaxPath]; + length = length < MaxPath - 1 ? length : MaxPath - 1; + + ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); + + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + struct stat buffer; + mode_t mode; + if(stat(pathCopy, &buffer)) { + return 0; + } + mode = buffer.st_mode; + mode = mode & 07555; + + return chmod(pathCopy, mode) == 0; +} + +static jobject java_io_File_listImpl(JNIEnv* env, jclass clazz, + jbyteArray path) { + + struct dirEntry { + char pathEntry[MaxPath]; + struct dirEntry *next; + } *dirList, *currentEntry; + + jsize length = (*env)->GetArrayLength(env, path); + char pathCopy[MaxPath]; + char filename[MaxPath]; + jint result = 0, index; + jint numEntries = 0; + jarray answer = NULL; + jclass javaClass = NULL; + + dirList = NULL; + currentEntry = NULL; + + length = length < MaxPath - 1 ? length : MaxPath - 1; + ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); + if(length >= 1 && pathCopy[length - 1] != '\\' + && pathCopy[length - 1] != '/') { + pathCopy[length] = '/'; + length++; + } + pathCopy[length] = '\0'; + + convertToPlatform(pathCopy); + + DIR *dirp = NULL; + struct dirent *entry; + + dirp = opendir(pathCopy); + + if(dirp == NULL) { + return NULL; + } + + entry = readdir(dirp); + + if(entry == NULL) { + closedir(dirp); + return NULL; + } + strcpy(filename, entry->d_name); + + while(result > -1) { + if(strcmp(".", filename) != 0 && (strcmp("..", filename) != 0)) { + if(numEntries > 0) { + currentEntry->next = + (struct dirEntry *) malloc(sizeof(struct dirEntry)); + currentEntry = currentEntry->next; + } else { + dirList = (struct dirEntry *) malloc(sizeof(struct dirEntry)); + currentEntry = dirList; + } + if(currentEntry == NULL) { + closedir(dirp); + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + goto cleanup; + } + strcpy(currentEntry->pathEntry, filename); + numEntries++; + } + + entry = readdir(dirp); + + if(entry == NULL) { + result = -1; + } else { + strcpy(filename, entry->d_name); + } + } + closedir(dirp); + + if(numEntries == 0) { + return NULL; + } + + javaClass = (*env)->FindClass(env, "[B"); + if(javaClass == NULL) { + return NULL; + } + answer = (*env)->NewObjectArray(env, numEntries, javaClass, NULL); + +cleanup: + for(index = 0; index < numEntries; index++) + { + jbyteArray entrypath; + jsize entrylen = strlen(dirList->pathEntry); + currentEntry = dirList; + if(answer) + { + entrypath = (*env)->NewByteArray(env, entrylen); + (*env)->SetByteArrayRegion(env, entrypath, 0, entrylen, + (jbyte *) dirList->pathEntry); + (*env)->SetObjectArrayElement(env, answer, index, entrypath); + (*env)->DeleteLocalRef(env, entrypath); + } + dirList = dirList->next; + free((void *)currentEntry); + } + + return answer; +} + +static jboolean java_io_File_mkdirImpl(JNIEnv* env, jobject recv, + jbyteArray path) { + jint result; + char pathCopy[MaxPath]; + jsize length = (*env)->GetArrayLength(env, path); + length = length < MaxPath - 1 ? length : MaxPath - 1; + ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + +// BEGIN android-changed +// don't want default permissions to allow global access. + result = mkdir(pathCopy, S_IRWXU); +// END android-changed + + if(-1 != result) + { + result = 0; + } + + return result == 0; +} + +static jint java_io_File_newFileImpl(JNIEnv* env, jobject recv, + jbyteArray path) { + + if(path == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return -1; + } + + jint result; + jsize length = (*env)->GetArrayLength(env, path); + char pathCopy[MaxPath]; + length = length < MaxPath - 1 ? length : MaxPath - 1; + (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); + pathCopy[length] = '\0'; + convertToPlatform(pathCopy); + + /* First check to see if file already exists */ + if(java_io_File_existsImpl(env, recv, path)) + { + return 1; + } + + /* Now create the file and close it */ +// BEGIN android-changed +// don't want default permissions to allow global access. + int fd = open(pathCopy, O_EXCL | O_CREAT, 0600); +// END android-changed + if(fd == -1) + { + if(errno == EEXIST) { + return 1; + } + return -1; + } + close(fd); + + return 0; +} + +static jboolean java_io_File_renameToImpl(JNIEnv* env, jobject recv, + jbyteArray pathExist, jbyteArray pathNew) { + jint result; + jsize length; + char pathExistCopy[MaxPath], pathNewCopy[MaxPath]; + + length = (*env)->GetArrayLength(env, pathExist); + length = length < MaxPath - 1 ? length : MaxPath - 1; + ((*env)->GetByteArrayRegion(env, pathExist, 0, length, + (jbyte *)pathExistCopy)); + pathExistCopy[length] = '\0'; + + length = (*env)->GetArrayLength(env, pathNew); + length = length < MaxPath - 1 ? length : MaxPath - 1; + ((*env)->GetByteArrayRegion(env, pathNew, 0, length, + (jbyte *)pathNewCopy)); + pathNewCopy[length] = '\0'; + + convertToPlatform(pathExistCopy); + convertToPlatform(pathNewCopy); + + result = rename(pathExistCopy, pathNewCopy); + + return result == 0; +} + +static void java_io_File_oneTimeInitialization(JNIEnv * env, jclass clazz) +{ + // dummy to stay compatible to harmony +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "rootsImpl", "()[[B", (void*) java_io_File_rootsImpl }, + { "deleteDirImpl", "([B)Z", (void*) java_io_File_deleteDirImpl }, + { "deleteFileImpl", "([B)Z", (void*) java_io_File_deleteFileImpl }, + { "existsImpl", "([B)Z", (void*) java_io_File_existsImpl }, + { "getCanonImpl", "([B)[B", (void*) java_io_File_getCanonImpl }, + { "isDirectoryImpl", "([B)Z", (void*) java_io_File_isDirectoryImpl }, + { "isFileImpl", "([B)Z", (void*) java_io_File_isFileImpl }, + { "isHiddenImpl", "([B)Z", (void*) java_io_File_isHiddenImpl }, + // BEGIN android-changed + { "isReadableImpl", "([B)Z", (void*) java_io_File_readable }, + { "isWriteableImpl", "([B)Z", (void*) java_io_File_writable }, + // END android-changed + { "getLinkImpl", "([B)[B", (void*) java_io_File_getLinkImpl }, + { "lastModifiedImpl", "([B)J", (void*) java_io_File_lastModifiedImpl }, + { "setReadOnlyImpl", "([B)Z", (void*) java_io_File_setReadOnlyImpl }, + { "lengthImpl", "([B)J", (void*) java_io_File_lengthImpl }, + { "listImpl", "([B)[[B",(void*) java_io_File_listImpl }, + { "mkdirImpl", "([B)Z", (void*) java_io_File_mkdirImpl }, + { "newFileImpl", "([B)I", (void*) java_io_File_newFileImpl }, + { "renameToImpl", "([B[B)Z",(void*) java_io_File_renameToImpl }, + { "setLastModifiedImpl","([BJ)Z", + (void*) java_io_File_setLastModifiedImpl }, + { "oneTimeInitialization","()V", + (void*) java_io_File_oneTimeInitialization } +}; +int register_java_io_File(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "java/io/File", + gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/java_io_FileDescriptor.c b/luni/src/main/native/java_io_FileDescriptor.c new file mode 100644 index 0000000..a5ec8b7 --- /dev/null +++ b/luni/src/main/native/java_io_FileDescriptor.c @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JNIHelp.h" + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <sys/ioctl.h> + +/* + * These are JNI field IDs for the stuff we're interested in. They're + * computed when the class is loaded. + */ +static struct { + jfieldID descriptor; /* int */ + jmethodID constructorInt; + jmethodID setFD; + jclass clazz; +} gCachedFields; + +/* + * Internal helper function. + * + * Get the file descriptor. + */ +static inline int getFd(JNIEnv* env, jobject obj) +{ + return (*env)->GetIntField(env, obj, gCachedFields.descriptor); +} + +/* + * Internal helper function. + * + * Set the file descriptor. + */ +static inline void setFd(JNIEnv* env, jobject obj, jint value) +{ + (*env)->SetIntField(env, obj, gCachedFields.descriptor, value); +} + +/* + * native private static void nativeClassInit() + * + * Perform one-time initialization. If the class is unloaded and re-loaded, + * this will be called again. + */ +static void nativeClassInit(JNIEnv* env, jclass clazz) +{ + gCachedFields.clazz = (*env)->NewGlobalRef(env, clazz); + + gCachedFields.descriptor = + (*env)->GetFieldID(env, clazz, "descriptor", "I"); + + if(gCachedFields.descriptor == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", "FileDescriptor"); + return; + } + + gCachedFields.constructorInt = + (*env)->GetMethodID(env, clazz, "<init>", "()V"); + + if(gCachedFields.constructorInt == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "<init>()V"); + return; + } +} + +/* + * public native void sync() + */ +static void fd_sync(JNIEnv* env, jobject obj) { + int fd = getFd(env, obj); + + if (fsync(fd) != 0) { + /* + * If fd is a socket, then fsync(fd) is defined to fail with + * errno EINVAL. This isn't actually cause for concern. + * TODO: Look into not bothering to call fsync() at all if + * we know we are dealing with a socket. + */ + if (errno != EINVAL) { + jniThrowException(env, "java/io/SyncFailedException", ""); + } + } +} + +/* + * public native boolean valid() + */ +static jboolean fd_valid(JNIEnv* env, jobject obj) { + int fd = getFd(env, obj); + struct stat sb; + + if(fstat(fd, &sb) == 0) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} + +/* checks to see if class is inited and inits if needed, returning -1 + * on fail and 0 on success + */ +static int checkClassInit (JNIEnv *env) { + if(gCachedFields.clazz == NULL) { + /* this should cause the class to be inited and + * our static variables to be filled in + * + * (Note that FindClass just loads the class; it doesn't get + * initialized until we try to do something with it.) + */ + jclass clazz; + clazz = (*env)->FindClass(env, "java/io/FileDescriptor"); + if(clazz == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.io.FileDescriptor"); + return -1; + } + + jfieldID readWriteId; + readWriteId = (*env)->GetStaticFieldID(env, clazz, "in", + "Ljava/io/FileDescriptor;"); + if(readWriteId == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldException", + "FileDescriptor.readOnly(Z)"); + return -1; + } + + (void) (*env)->GetStaticObjectField(env, clazz, readWriteId); + } + + return 0; +} + + +/* + * For JNIHelp.c + * Create a java.io.FileDescriptor given an integer fd + */ + +jobject jniCreateFileDescriptor (JNIEnv *env, int fd) { + jobject ret; + + /* the class may not have been loaded yet */ + if(checkClassInit(env) < 0) { + return NULL; + } + + ret = (*env)->NewObject(env, gCachedFields.clazz, + gCachedFields.constructorInt); + + (*env)->SetIntField(env, ret, gCachedFields.descriptor, fd); + + return ret; +} + +/* + * For JNIHelp.c + * Get an int file descriptor from a java.io.FileDescriptor + */ + +int jniGetFDFromFileDescriptor (JNIEnv* env, jobject fileDescriptor) { + /* should already be initialized if it's an actual FileDescriptor */ + assert(fileDescriptor != NULL); + assert(gCachedFields.clazz != NULL); + + return getFd(env, fileDescriptor); +} + +/* + * For JNIHelp.c + * Set the descriptor of a java.io.FileDescriptor + */ + +void jniSetFileDescriptorOfFD (JNIEnv* env, jobject fileDescriptor, int value) { + /* should already be initialized if it's an actual FileDescriptor */ + assert(fileDescriptor != NULL); + assert(gCachedFields.clazz != NULL); + + setFd(env, fileDescriptor, value); +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "oneTimeInitialization", "()V", nativeClassInit }, + { "syncImpl", "()V", fd_sync }, + { "valid", "()Z", fd_valid } +}; +int register_java_io_FileDescriptor(JNIEnv* env) { + return jniRegisterNativeMethods(env, "java/io/FileDescriptor", + gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/java_io_ObjectInputStream.c b/luni/src/main/native/java_io_ObjectInputStream.c new file mode 100644 index 0000000..3519055 --- /dev/null +++ b/luni/src/main/native/java_io_ObjectInputStream.c @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" + +static void java_setFieldBool (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jboolean newValue) { + const char *fieldNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "Z"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid != 0) { + (*env)->SetBooleanField(env, targetObject, fid, newValue); + } +} + +static void java_setFieldChar (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jchar newValue) { + const char *fieldNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "C"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid != 0) { + (*env)->SetCharField(env, targetObject, fid, newValue); + } +} + +static void java_setFieldInt (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jint newValue) { + const char *fieldNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "I"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid != 0) { + (*env)->SetIntField(env, targetObject, fid, newValue); + } +} + +static void java_setFieldFloat (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jfloat newValue) { + const char *fieldNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "F"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid != 0) { + (*env)->SetFloatField(env, targetObject, fid, newValue); + } +} + +static void java_setFieldDouble (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jdouble newValue) { + const char *fieldNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "D"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid != 0) { + (*env)->SetDoubleField(env, targetObject, fid, newValue); + } + +} + +static void java_setFieldShort (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jshort newValue) { + const char *fieldNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "S"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid != 0) { + (*env)->SetShortField(env, targetObject, fid, newValue); + } + +} + +static void java_setFieldLong (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jlong newValue) { + const char *fieldNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "J"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid != 0) { + (*env)->SetLongField(env, targetObject, fid, newValue); + } +} + +static jobject java_newInstance (JNIEnv * env, jclass clazz, + jclass instantiationClass, + jclass constructorClass) { + jmethodID mid = + (*env)->GetMethodID(env, constructorClass, "<init>", "()V"); + + if(mid == 0) { + /* Cant newInstance,No empty constructor... */ + return (jobject) 0; + } else { + /* Instantiate an object of a given class */ + return (jobject) (*env)->NewObject(env, instantiationClass, mid); + } + +} + +static void java_setFieldByte (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jbyte newValue){ + const char *fieldNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "B"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* Two options now. Maybe getFieldID caused an exception, or maybe it returned the real value */ + if(fid != 0) { + (*env)->SetByteField(env, targetObject, fid, newValue); + } +} + +static void java_setFieldObj (JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jstring fieldTypeName, + jobject newValue) { + const char *fieldNameInC, *fieldTypeNameInC; + jfieldID fid; + if(targetObject == NULL) { + return; + } + fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + fieldTypeNameInC = (*env)->GetStringUTFChars(env, fieldTypeName, NULL); + fid = (*env)->GetFieldID(env, declaringClass, + fieldNameInC, fieldTypeNameInC); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + (*env)->ReleaseStringUTFChars(env, fieldTypeName, fieldTypeNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid != 0) { + (*env)->SetObjectField(env, targetObject, fid, newValue); + } +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "setField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;J)V", + (void*) java_setFieldLong }, + { "setField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;S)V", + (void*) java_setFieldShort }, + { "setField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;D)V", + (void*) java_setFieldDouble }, + { "setField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Z)V", + (void*) java_setFieldBool }, + { "setField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;B)V", + (void*) java_setFieldByte }, + { "setField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;F)V", + (void*) java_setFieldFloat }, + { "setField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;C)V", + (void*) java_setFieldChar }, + { "setField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;I)V", + (void*) java_setFieldInt }, + { "newInstance", + "(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;", + (void*) java_newInstance }, + { "objSetField", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V", + (void*) java_setFieldObj } + +}; +int register_java_io_ObjectInputStream(JNIEnv* env) { + return jniRegisterNativeMethods(env, "java/io/ObjectInputStream", + gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/java_io_ObjectOutputStream.c b/luni/src/main/native/java_io_ObjectOutputStream.c new file mode 100644 index 0000000..e465bc2 --- /dev/null +++ b/luni/src/main/native/java_io_ObjectOutputStream.c @@ -0,0 +1,246 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" + +static jlong java_getFieldLong(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "J"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jlong) 0L; + } else { + return (*env)->GetLongField (env, targetObject, fid); + } +} + +static jshort java_getFieldShort(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "S"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jshort) 0; + } else { + return (*env)->GetShortField (env, targetObject, fid); + } +} + +static jdouble java_getFieldDouble(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "D"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jdouble) 0.0; + } else { + return (*env)->GetDoubleField (env, targetObject, fid); + } +} + +static jboolean java_getFieldBool(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "Z"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jboolean) 0; + } else { + return (*env)->GetBooleanField (env, targetObject, fid); + } +} + +static jbyte java_getFieldByte(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "B"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jbyte) 0; + } else { + return (*env)->GetByteField (env, targetObject, fid); + } +} + +static jfloat java_getFieldFloat(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "F"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jfloat) 0.0f; + } + else { + return (*env)->GetFloatField (env, targetObject, fid); + } + +} + +static jchar java_getFieldChar(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "C"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jchar) 0; + } else { + return (*env)->GetCharField(env, targetObject, fid); + } +} + +static jobject java_getFieldObj(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName, + jstring fieldTypeName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + const char *fieldTypeNameInC = + (*env)->GetStringUTFChars(env, fieldTypeName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, + fieldNameInC, fieldTypeNameInC); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + (*env)->ReleaseStringUTFChars(env, fieldTypeName, fieldTypeNameInC); + + /* + * Two options now. Maybe getFieldID caused an exception, + * or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jobject) 0; + } else { + return (*env)->GetObjectField (env, targetObject, fid); + } +} + +static jint java_getFieldInt(JNIEnv * env, jclass clazz, + jobject targetObject, + jclass declaringClass, + jstring fieldName) { + const char *fieldNameInC = (*env)->GetStringUTFChars(env, fieldName, NULL); + jfieldID fid = (*env)->GetFieldID(env, declaringClass, fieldNameInC, "I"); + (*env)->ReleaseStringUTFChars(env, fieldName, fieldNameInC); + + /* + * Two options now. Maybe getFieldID caused + * an exception, or maybe it returned the real value + */ + if(fid == 0) { + // Field not found. I believe we must throw an exception here + return (jint) 0; + } else { + return (*env)->GetIntField(env, targetObject, fid); + } +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "getFieldLong", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)J", + (void*) java_getFieldLong }, + { "getFieldShort", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)S", + (void*) java_getFieldShort }, + { "getFieldDouble", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)D", + (void*) java_getFieldDouble }, + { "getFieldBool", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)Z", + (void*) java_getFieldBool }, + { "getFieldByte", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)B", + (void*) java_getFieldByte }, + { "getFieldFloat", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)F", + (void*) java_getFieldFloat }, + { "getFieldChar", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)C", + (void*) java_getFieldChar }, + { "getFieldObj", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", + (void*) java_getFieldObj }, + { "getFieldInt", + "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)I", + (void*) java_getFieldInt }, + +}; +int register_java_io_ObjectOutputStream(JNIEnv* env) { + return jniRegisterNativeMethods(env, "java/io/ObjectOutputStream", + gMethods, NELEM(gMethods)); +} + diff --git a/luni/src/main/native/java_io_ObjectStreamClass.c b/luni/src/main/native/java_io_ObjectStreamClass.c new file mode 100644 index 0000000..c3fb518 --- /dev/null +++ b/luni/src/main/native/java_io_ObjectStreamClass.c @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" + +static jobject java_io_osc_getFieldSignature(JNIEnv * env, jclass clazz, + jobject reflectField) { + jclass lookupClass; + jmethodID mid; + + lookupClass = (*env)->FindClass(env, "java/lang/reflect/Field"); + if(!lookupClass) { + return NULL; + } + + mid = (*env)->GetMethodID(env, lookupClass, "getSignature", + "()Ljava/lang/String;"); + if(!mid) + { + return NULL; + } + + jclass fieldClass = (*env)->GetObjectClass(env, reflectField); + + return (*env)->CallNonvirtualObjectMethod(env, reflectField, + fieldClass, mid); +} + +static jobject java_io_osc_getMethodSignature(JNIEnv * env, jclass clazz, + jobject reflectMethod) +{ + jclass lookupClass; + jmethodID mid; + + lookupClass = (*env)->FindClass(env, "java/lang/reflect/Method"); + if(!lookupClass) { + return NULL; + } + + mid = (*env)->GetMethodID(env, lookupClass, "getSignature", + "()Ljava/lang/String;"); + if(!mid) { + return NULL; + } + + jclass methodClass = (*env)->GetObjectClass(env, reflectMethod); + return (*env)->CallNonvirtualObjectMethod(env, reflectMethod, + methodClass, mid); +} + +static jobject java_io_osc_getConstructorSignature(JNIEnv * env, + jclass clazz, + jobject + reflectConstructor) +{ + jclass lookupClass; + jmethodID mid; + + lookupClass = (*env)->FindClass(env, "java/lang/reflect/Constructor"); + if(!lookupClass) { + return NULL; + } + + mid = (*env)->GetMethodID(env, lookupClass, "getSignature", + "()Ljava/lang/String;"); + if(!mid) { + return NULL; + } + + jclass constructorClass = (*env)->GetObjectClass(env, reflectConstructor); + return (*env)->CallNonvirtualObjectMethod(env, reflectConstructor, + constructorClass, mid); +} + +static jboolean java_io_osc_hasClinit(JNIEnv * env, jclass clazz, + jobject targetClass) { + jmethodID mid = (*env)->GetStaticMethodID(env, targetClass, + "<clinit>", "()V"); + (*env)->ExceptionClear(env); + + /* + * Can I just return mid and rely on typecast to convert to jboolean ? + * Safe implementation for now + */ + if(mid == 0) { + /* No <clinit>... */ + return (jboolean) 0; + } else { + return (jboolean) 1; + } +} + +static void java_io_osc_oneTimeInitialization(JNIEnv * env, jclass clazz) { + // dummy to stay compatible to harmony +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "getFieldSignature", + "(Ljava/lang/reflect/Field;)Ljava/lang/String;", + (void*) java_io_osc_getFieldSignature }, + { "getMethodSignature", + "(Ljava/lang/reflect/Method;)Ljava/lang/String;", + (void*) java_io_osc_getMethodSignature }, + { "getConstructorSignature", + "(Ljava/lang/reflect/Constructor;)Ljava/lang/String;", + (void*) java_io_osc_getConstructorSignature }, + { "hasClinit", "(Ljava/lang/Class;)Z", + (void*) java_io_osc_hasClinit }, + { "oneTimeInitialization", "()V", + (void*) java_io_osc_oneTimeInitialization } +}; +int register_java_io_ObjectStreamClass(JNIEnv* env) { + return jniRegisterNativeMethods(env, "java/io/ObjectStreamClass", + gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/java_lang_Character.cpp b/luni/src/main/native/java_lang_Character.cpp new file mode 100644 index 0000000..c1324d6 --- /dev/null +++ b/luni/src/main/native/java_lang_Character.cpp @@ -0,0 +1,75 @@ +// +// java_lang_Character.cpp +// Android +// +// Copyright 2006 The Android Open Source Project +// +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" + +//#define LOG_TAG "Character" +//#include "utils/Log.h" +#include "utils/AndroidUnicode.h" + +#include <stdlib.h> + + +using namespace android; + +/* + * native private static int nativeGetData(int c) + */ +static jint getData(JNIEnv* env, jclass clazz, jint val) +{ + return Unicode::getPackedData(val); +} + +/* + * native private static int nativeToLower(int c) + */ +static jint toLower(JNIEnv* env, jclass clazz, jint val) +{ + return Unicode::toLower(val); +} + +/* + * native private static int nativeToUpper(int c) + */ +static jint toUpper(JNIEnv* env, jclass clazz, jint val) +{ + return Unicode::toUpper(val); +} + +/* + * native private static int nativeNumericValue(int c) + */ +static jint numericValue(JNIEnv* env, jclass clazz, jint val) +{ + return Unicode::getNumericValue(val); +} + +/* + * native private static int nativeToTitle(int c) + */ +static jint toTitle(JNIEnv* env, jclass clazz, jint val) +{ + return Unicode::toTitle(val); +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "nativeGetData", "(I)I", (void*) getData }, + { "nativeToLower", "(I)I", (void*) toLower }, + { "nativeToUpper", "(I)I", (void*) toUpper }, + { "nativeNumericValue", "(I)I", (void*) numericValue }, + { "nativeToTitle", "(I)I", (void*) toTitle }, +}; +int register_java_lang_Character(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "java/lang/Character", + gMethods, NELEM(gMethods)); +} + diff --git a/luni/src/main/native/java_lang_Double.c b/luni/src/main/native/java_lang_Double.c new file mode 100644 index 0000000..fd4b7f1 --- /dev/null +++ b/luni/src/main/native/java_lang_Double.c @@ -0,0 +1,77 @@ +// +// java_lang_Double.c +// Android +// +// Copyright 2005 The Android Open Source Project +// +#include "JNIHelp.h" + +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +typedef union { + uint64_t bits; + double d; +} Double; + +#define NaN (0x7ff8000000000000ULL) + +/* + * public static native long doubleToLongBits(double value) + */ +static jlong doubleToLongBits(JNIEnv* env, jclass clazz, jdouble val) +{ + Double d; + + d.d = val; + + // For this method all values in the NaN range are + // normalized to the canonical NaN value. + + if (isnan(d.d)) + d.bits = NaN; + + return d.bits; +} + +/* + * public static native long doubleToRawLongBits(double value) + */ +static jlong doubleToRawLongBits(JNIEnv* env, jclass clazz, jdouble val) +{ + Double d; + + d.d = val; + + return d.bits; +} + +/* + * public static native double longBitsToDouble(long bits) + */ +static jdouble longBitsToDouble(JNIEnv* env, jclass clazz, jlong val) +{ + Double d; + + d.bits = val; + + return d.d; +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "doubleToLongBits", "(D)J", doubleToLongBits }, + { "doubleToRawLongBits", "(D)J", doubleToRawLongBits }, + { "longBitsToDouble", "(J)D", longBitsToDouble }, +}; +int register_java_lang_Double(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "java/lang/Double", + gMethods, NELEM(gMethods)); +} + diff --git a/luni/src/main/native/java_lang_Float.c b/luni/src/main/native/java_lang_Float.c new file mode 100644 index 0000000..2a7af21 --- /dev/null +++ b/luni/src/main/native/java_lang_Float.c @@ -0,0 +1,85 @@ +// +// java_lang_Float.c +// Android +// +// Copyright 2005 The Android Open Source Project +// +#include "JNIHelp.h" + +#include <math.h> +#include <stdlib.h> +#include <stdio.h> + +typedef union { + unsigned int bits; + float f; +} Float; + +#define NaN (0x7fc00000) + +/* + * Local helper function. + */ +static int IsNaN(unsigned bits) +{ + return ((bits >= 0x7f800001U && bits <= 0x7fffffffU) + || (bits >= 0xff800001U && bits <= 0xffffffffU)); +} + +/* + * public static native int floatToIntBits(float value) + */ +static jint floatToIntBits(JNIEnv* env, jclass clazz, jfloat val) +{ + Float f; + + f.f = val; + + // For this method all values in the NaN range are + // normalized to the canonical NaN value. + + if (IsNaN(f.bits)) + f.bits = NaN; + + return f.bits; +} + +/* + * public static native int floatToRawBits(float value) + */ +static jint floatToRawBits(JNIEnv* env, jclass clazz, jfloat val) +{ + Float f; + + f.f = val; + + return f.bits; +} + +/* + * public static native float intBitsToFloat(int bits) + */ +static jfloat intBitsToFloat(JNIEnv* env, jclass clazz, jint val) +{ + Float f; + + f.bits = val; + + return f.f; +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "floatToIntBits", "(F)I", floatToIntBits }, + { "floatToRawIntBits", "(F)I", floatToRawBits }, + { "intBitsToFloat", "(I)F", intBitsToFloat }, +}; +int register_java_lang_Float(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "java/lang/Float", + gMethods, NELEM(gMethods)); +} + diff --git a/luni/src/main/native/java_lang_Math.c b/luni/src/main/native/java_lang_Math.c new file mode 100644 index 0000000..a3a69dd --- /dev/null +++ b/luni/src/main/native/java_lang_Math.c @@ -0,0 +1,204 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Native functions for java.lang.Math. + */ +#include "jni.h" +#include "JNIHelp.h" + +#include <stdlib.h> +#include <math.h> + +/* native public static double sin(double a); */ +static jdouble jsin(JNIEnv* env, jclass clazz, jdouble a) +{ + return sin(a); +} + +/* native public static double cos(double a); */ +static jdouble jcos(JNIEnv* env, jclass clazz, jdouble a) +{ + return cos(a); +} + +/* native public static double tan(double a); */ +static jdouble jtan(JNIEnv* env, jclass clazz, jdouble a) +{ + return tan(a); +} + +/* native public static double asin(double a); */ +static jdouble jasin(JNIEnv* env, jclass clazz, jdouble a) +{ + return asin(a); +} + +/* native public static double acos(double a); */ +static jdouble jacos(JNIEnv* env, jclass clazz, jdouble a) +{ + return acos(a); +} + +/* native public static double atan(double a); */ +static jdouble jatan(JNIEnv* env, jclass clazz, jdouble a) +{ + return atan(a); +} + +/* native public static double exp(double a); */ +static jdouble jexp(JNIEnv* env, jclass clazz, jdouble a) +{ + return exp(a); +} + +/* native public static double log(double a); */ +static jdouble jlog(JNIEnv* env, jclass clazz, jdouble a) +{ + return log(a); +} + +/* native public static double sqrt(double a); */ +static jdouble jsqrt(JNIEnv* env, jclass clazz, jdouble a) +{ + return sqrt(a); +} + +/* native public static double IEEEremainder(double a, double b); */ +static jdouble jieee_remainder(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return remainder(a, b); +} + +/* native public static double floor(double a); */ +static jdouble jfloor(JNIEnv* env, jclass clazz, jdouble a) +{ + return floor(a); +} + +/* native public static double ceil(double a); */ +static jdouble jceil(JNIEnv* env, jclass clazz, jdouble a) +{ + return ceil(a); +} + +/* native public static double rint(double a); */ +static jdouble jrint(JNIEnv* env, jclass clazz, jdouble a) +{ + return rint(a); +} + +/* native public static double atan2(double a, double b); */ +static jdouble jatan2(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return atan2(a, b); +} + +/* native public static double pow(double a, double b); */ +static jdouble jpow(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return pow(a, b); +} + +/* native public static double sinh(double a); */ +static jdouble jsinh(JNIEnv* env, jclass clazz, jdouble a) +{ + return sinh(a); +} + +/* native public static double tanh(double a); */ +static jdouble jtanh(JNIEnv* env, jclass clazz, jdouble a) +{ + return tanh(a); +} + +/* native public static double cosh(double a); */ +static jdouble jcosh(JNIEnv* env, jclass clazz, jdouble a) +{ + return cosh(a); +} + +/* native public static double log10(double a); */ +static jdouble jlog10(JNIEnv* env, jclass clazz, jdouble a) +{ + return log10(a); +} + +/* native public static double cbrt(double a); */ +static jdouble jcbrt(JNIEnv* env, jclass clazz, jdouble a) +{ + return cbrt(a); +} + +/* native public static double expm1(double a); */ +static jdouble jexpm1(JNIEnv* env, jclass clazz, jdouble a) +{ + return expm1(a); +} + +/* native public static double hypot(double a, double b); */ +static jdouble jhypot(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return hypot(a, b); +} + +/* native public static double log1p(double a); */ +static jdouble jlog1p(JNIEnv* env, jclass clazz, jdouble a) +{ + return log1p(a); +} + +/* native public static double nextafter(double a, double b); */ +static jdouble jnextafter(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return nextafter(a, b); +} + +/* native public static float nextafterf(float a, float b); */ +static jfloat jnextafterf(JNIEnv* env, jclass clazz, jfloat a, jfloat b) +{ + return nextafterf(a, b); +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "sin", "(D)D", jsin }, + { "cos", "(D)D", jcos }, + { "tan", "(D)D", jtan }, + + { "asin", "(D)D", jasin }, + { "acos", "(D)D", jacos }, + { "atan", "(D)D", jatan }, + + { "exp", "(D)D", jexp }, + { "log", "(D)D", jlog }, + { "sqrt", "(D)D", jsqrt }, + + { "IEEEremainder", "(DD)D", jieee_remainder }, + + { "floor", "(D)D", jfloor }, + { "ceil", "(D)D", jceil }, + { "rint", "(D)D", jrint }, + + { "atan2", "(DD)D", jatan2 }, + { "pow", "(DD)D", jpow }, + + { "sinh", "(D)D", jsinh }, + { "cosh", "(D)D", jcosh }, + { "tanh", "(D)D", jtanh }, + { "log10", "(D)D", jlog10 }, + { "cbrt", "(D)D", jcbrt }, + { "expm1", "(D)D", jexpm1 }, + { "hypot", "(DD)D", jhypot }, + { "log1p", "(D)D", jlog1p }, + { "nextafter", "(DD)D", jnextafter }, + { "nextafterf", "(FF)F", jnextafterf }, +}; + +int register_java_lang_Math(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "java/lang/Math", gMethods, + NELEM(gMethods)); +} diff --git a/luni/src/main/native/java_lang_StrictMath.c b/luni/src/main/native/java_lang_StrictMath.c new file mode 100644 index 0000000..7d335f7 --- /dev/null +++ b/luni/src/main/native/java_lang_StrictMath.c @@ -0,0 +1,229 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Native functions for java.lang.StrictMath. + */ +#include "jni.h" +#include "JNIHelp.h" + +#include <stdlib.h> +/* This static way is the "best" way to integrate fdlibm without a conflict + * into the android envoirement + */ + +/* #include "fltconst.h" */ + +#if defined(__P) +#undef __P +#endif /* defined(__P) */ + +#include "../../external/fdlibm/fdlibm.h" +_LIB_VERSION_TYPE _LIB_VERSION = _IEEE_; + +/* native public static double sin(double a); */ +static jdouble jsin(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_sin(a); +} + +/* native public static double cos(double a); */ +static jdouble jcos(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_cos(a); +} + +/* native public static double tan(double a); */ +static jdouble jtan(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_tan(a); +} + +/* native public static double asin(double a); */ +static jdouble jasin(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_asin(a); +} + +/* native public static double acos(double a); */ +static jdouble jacos(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_acos(a); +} + +/* native public static double atan(double a); */ +static jdouble jatan(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_atan(a); +} + +/* native public static double exp(double a); */ +static jdouble jexp(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_exp(a); +} + +/* native public static double log(double a); */ +static jdouble jlog(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_log(a); +} + +/* native public static double sqrt(double a); */ +static jdouble jsqrt2(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_sqrt(a); +} + +/* native public static double IEEEremainder(double a, double b); */ +static jdouble jieee_remainder(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return ieee_remainder(a, b); +} + +/* native public static double floor(double a); */ +static jdouble jfloor(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_floor(a); +} + +/* native public static double ceil(double a); */ +static jdouble jceil(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_ceil(a); +} + +/* native public static double rint(double a); */ +static jdouble jrint(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_rint(a); +} + +/* native public static double atan2(double a, double b); */ +static jdouble jatan2(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return ieee_atan2(a, b); +} + +/* native public static double pow(double a, double b); */ +static jdouble jpow(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return ieee_pow(a,b); +} + +/* native public static double sinh(double a); */ +static jdouble jsinh(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_sinh(a); +} + +/* native public static double tanh(double a); */ +static jdouble jtanh(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_tanh(a); +} + +/* native public static double cosh(double a); */ +static jdouble jcosh(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_cosh(a); +} + +/* native public static double log10(double a); */ +static jdouble jlog10(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_log10(a); +} + +/* native public static double cbrt(double a); */ +static jdouble jcbrt(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_cbrt(a); +} + +/* native public static double expm1(double a); */ +static jdouble jexpm1(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_expm1(a); +} + +/* native public static double hypot(double a, double b); */ +static jdouble jhypot(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return ieee_hypot(a, b); +} + +/* native public static double log1p(double a); */ +static jdouble jlog1p(JNIEnv* env, jclass clazz, jdouble a) +{ + return ieee_log1p(a); +} + +/* native public static double nextafter(double a, double b); */ +static jdouble jnextafter(JNIEnv* env, jclass clazz, jdouble a, jdouble b) +{ + return ieee_nextafter(a, b); +} + +/* native public static float nextafterf(float a, float b); */ +static jfloat jnextafterf(JNIEnv* env, jclass clazz, jfloat arg1, jfloat arg2) +{ + jint hx = *(jint*)&arg1; + jint hy = *(jint*)&arg2; + + if (!(hx&0x7fffffff)) { /* arg1 == 0 */ + *(jint*)&arg1 = (hy & 0x80000000) | 0x1; + return arg1; + } + + if((hx > 0) ^ (hx > hy)) { /* |arg1| < |arg2| */ + hx += 1; + } else { + hx -= 1; + } + *(jint*)&arg1 = hx; + return arg1; +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "sin", "(D)D", jsin }, + { "cos", "(D)D", jcos }, + { "tan", "(D)D", jtan }, + + { "asin", "(D)D", jasin }, + { "acos", "(D)D", jacos }, + { "atan", "(D)D", jatan }, + + { "exp", "(D)D", jexp }, + { "log", "(D)D", jlog }, + { "sqrt", "(D)D", jsqrt2 }, + + { "IEEEremainder", "(DD)D", jieee_remainder }, + + { "floor", "(D)D", jfloor }, + { "ceil", "(D)D", jceil }, + { "rint", "(D)D", jrint }, + + { "atan2", "(DD)D", jatan2 }, + { "pow", "(DD)D", jpow }, + + { "sinh", "(D)D", jsinh }, + { "cosh", "(D)D", jcosh }, + { "tanh", "(D)D", jtanh }, + { "log10", "(D)D", jlog10 }, + { "cbrt", "(D)D", jcbrt }, + { "expm1", "(D)D", jexpm1 }, + { "hypot", "(DD)D", jhypot }, + { "log1p", "(D)D", jlog1p }, + { "nextafter", "(DD)D", jnextafter }, + { "nextafterf", "(FF)F", jnextafterf }, +}; + +int register_java_lang_StrictMath(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "java/lang/StrictMath", gMethods, + NELEM(gMethods)); +} diff --git a/luni/src/main/native/java_net_InetAddress.cpp b/luni/src/main/native/java_net_InetAddress.cpp new file mode 100644 index 0000000..cf026bc --- /dev/null +++ b/luni/src/main/native/java_net_InetAddress.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InetAddress" + +#include "JNIHelp.h" +#include "utils/Log.h" +#include "jni.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <netdb.h> +#include <errno.h> + +#include <cutils/properties.h> +#include <cutils/adb_networking.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> + + +static jclass byteArrayClass = NULL; + +static jstring InetAddress_gethostname(JNIEnv* env, jobject obj) +{ + char name[256]; + int r = gethostname(name, 256); + if (r == 0) { + return env->NewStringUTF(name); + } else { + return NULL; + } +} + +static void throwNullPointerException(JNIEnv* env) +{ + const char* className = "java/lang/NullPointerException"; + + jclass exClass = env->FindClass(className); + + if (exClass == NULL) { + LOGE("Unable to find class %s", className); + } else { + env->ThrowNew(exClass, NULL); + } +} + +static void logIpString(struct addrinfo* ai, const char* name) +{ + char ipString[INET6_ADDRSTRLEN]; + int result = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipString, + sizeof(ipString), NULL, 0, NI_NUMERICHOST); + if (result == 0) { + LOGD("%s: %s (family %d, proto %d)", name, ipString, ai->ai_family, + ai->ai_protocol); + } else { + LOGE("%s: getnameinfo: %s", name, gai_strerror(result)); + } +} + +static jobjectArray getAllByNameUsingAdb(JNIEnv* env, const char* name) +{ + struct in_addr outaddr; + jobjectArray addressArray = NULL; + jbyteArray byteArray; + +#if 0 + LOGI("ADB networking: -gethostbyname err %d addr 0x%08x %u.%u.%u.%u", + err, (unsigned int)outaddr.a.s_addr, + outaddr.j[0],outaddr.j[1], + outaddr.j[2],outaddr.j[3]); +#endif + + if (adb_networking_gethostbyname(name, &outaddr) >= 0) { + addressArray = env->NewObjectArray(1, byteArrayClass, NULL); + byteArray = env->NewByteArray(4); + if (addressArray && byteArray) { + env->SetByteArrayRegion(byteArray, 0, 4, (jbyte*) &outaddr.s_addr); + env->SetObjectArrayElement(addressArray, 1, byteArray); + } + } + return addressArray; +} + +static jobjectArray getAllByNameUsingDns(JNIEnv* env, const char* name, + jboolean preferIPv4Stack) +{ + struct addrinfo hints, *addressList = NULL, *addrInfo; + jobjectArray addressArray = NULL; + + memset(&hints, 0, sizeof(hints)); + /* + * IPv4 only for now until the socket code supports IPv6; otherwise, the + * resolver will create two separate requests, one for IPv4 and one, + * currently unnecessary, for IPv6. + */ + hints.ai_family = AF_INET; + /* + * If we don't specify a socket type, every address will appear twice, once + * for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family + * anyway, just pick one. + */ + hints.ai_socktype = SOCK_STREAM; + + int result = getaddrinfo(name, NULL, &hints, &addressList); + if (result == 0 && addressList) { + // Count results so we know how to size the output array. + int addressCount = 0; + for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) { + if (addrInfo->ai_family == AF_INET || + addrInfo->ai_family == AF_INET6) { + addressCount++; + } + } + + // Prepare output array. + addressArray = env->NewObjectArray(addressCount, byteArrayClass, NULL); + if (addressArray == NULL) { + // Appropriate exception will be thrown. + LOGE("getAllByNameUsingDns: could not allocate output array"); + freeaddrinfo(addrInfo); + return NULL; + } + + // Examine returned addresses one by one, save them in the output array. + int index = 0; + for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) { + struct sockaddr* address = addrInfo->ai_addr; + size_t addressLength = 0; + void* rawAddress; + + switch (addrInfo->ai_family) { + // Find the raw address length and start pointer. + case AF_INET6: + addressLength = 16; + rawAddress = + &((struct sockaddr_in6*) address)->sin6_addr.s6_addr; + logIpString(addrInfo, name); + break; + case AF_INET: + addressLength = 4; + rawAddress = + &((struct sockaddr_in*) address)->sin_addr.s_addr; + logIpString(addrInfo, name); + break; + default: + // Unknown address family. Skip this address. + LOGE("getAllByNameUsingDns: Unknown address family %d", + addrInfo->ai_family); + continue; + } + + // Convert each IP address into a Java byte array. + jbyteArray bytearray = env->NewByteArray(addressLength); + if (bytearray == NULL) { + // Out of memory error will be thrown on return. + LOGE("getAllByNameUsingDns: Can't allocate %d-byte array", + addressLength); + addressArray = NULL; + break; + } + env->SetByteArrayRegion(bytearray, 0, addressLength, + (jbyte*) rawAddress); + env->SetObjectArrayElement(addressArray, index, bytearray); + env->DeleteLocalRef(bytearray); + index++; + } + } else if (result == EAI_SYSTEM && errno == EACCES) { + /* No permission to use network */ + jniThrowException( + env, "java/lang/SecurityException", + "Permission denied (maybe missing INTERNET permission)"); + } else { + // Do nothing. Return value will be null and the caller will throw an + // UnknownHostExeption. + } + + if (addressList) { + freeaddrinfo(addressList); + } + + return addressArray; +} + +jobjectArray InetAddress_getallbyname(JNIEnv* env, jobject obj, + jstring javaName, + jboolean preferIPv4Stack) +{ + if (javaName == NULL) { + throwNullPointerException(env); + return NULL; + } + + const char* name = env->GetStringUTFChars(javaName, NULL); + jobjectArray out = NULL; + + char useAdbNetworkingProperty[PROPERTY_VALUE_MAX]; + char adbConnected[PROPERTY_VALUE_MAX]; + property_get("android.net.use-adb-networking", + useAdbNetworkingProperty, ""); + property_get("adb.connected", + adbConnected, ""); + + // Any non-empty string value for use-adb-networking is considered "set" + if ((strlen(useAdbNetworkingProperty) > 0) + && (strlen(adbConnected) > 0) ) { + out = getAllByNameUsingAdb(env, name); + } else { + out = getAllByNameUsingDns(env, name, preferIPv4Stack); + } + + if (!out) { + LOGI("Unknown host %s, throwing UnknownHostException", name); + jniThrowException(env, "java/net/UnknownHostException", name); + } + env->ReleaseStringUTFChars(javaName, name); + return out; +} + + +static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj, + jbyteArray javaAddress) +{ + if (javaAddress == NULL) { + throwNullPointerException(env); + return NULL; + } + + size_t addrlen = env->GetArrayLength(javaAddress); + jbyte* rawAddress = env->GetByteArrayElements(javaAddress, NULL); + if (rawAddress == NULL) { + throwNullPointerException(env); + return NULL; + } + + // Convert the raw address bytes into a socket address structure. + struct sockaddr_storage ss; + struct sockaddr_in *sin = (struct sockaddr_in *) &ss; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss; + size_t socklen; + switch (addrlen) { + case 4: + socklen = sizeof(struct sockaddr_in); + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr.s_addr, rawAddress, 4); + break; + case 16: + socklen = sizeof(struct sockaddr_in6); + memset(sin6, 0, sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr.s6_addr, rawAddress, 4); + break; + default: + jniThrowException(env, "java/net/UnknownHostException", + "Invalid address length"); + return NULL; + } + + + // Convert the socket address structure to an IP string for logging. + int ret; + char ipstr[INET6_ADDRSTRLEN]; + ret = getnameinfo((struct sockaddr *) &ss, socklen, ipstr, sizeof(ipstr), + NULL, 0, NI_NUMERICHOST); + if (ret) { + LOGE("gethostbyaddr: getnameinfo: %s", gai_strerror(ret)); + return NULL; + } + + // Look up the IP address from the socket address structure. + jstring result = NULL; + char name[NI_MAXHOST]; + ret = getnameinfo((struct sockaddr *) &ss, socklen, name, sizeof(name), + NULL, 0, 0); + if (ret == 0) { + LOGI("gethostbyaddr: getnameinfo: %s = %s", ipstr, name); + result = env->NewStringUTF(name); + } else { + LOGE("gethostbyaddr: getnameinfo: unknown host %s: %s", ipstr, + gai_strerror(ret)); + } + + return result; +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "gethostbyaddr", "([B)Ljava/lang/String;", + (void*) InetAddress_gethostbyaddr }, + { "getallbyname", "(Ljava/lang/String;Z)[[B", + (void*) InetAddress_getallbyname }, + { "gethostname", "()Ljava/lang/String;", + (void*) InetAddress_gethostname }, +}; + +extern "C" int register_java_net_InetAddress(JNIEnv* env) +{ + jclass tempClass = env->FindClass("[B"); + if (tempClass) { + byteArrayClass = (jclass) env->NewGlobalRef(tempClass); + } + if (!byteArrayClass) { + LOGE("register_java_net_InetAddress: cannot allocate byte array class"); + return -1; + } + return jniRegisterNativeMethods(env, "java/net/InetAddress", + gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/java_net_NetworkInterface.c b/luni/src/main/native/java_net_NetworkInterface.c new file mode 100644 index 0000000..379ccdf --- /dev/null +++ b/luni/src/main/native/java_net_NetworkInterface.c @@ -0,0 +1,844 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JNIHelp.h" +#include "jni.h" +#include "errno.h" + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <sys/ioctl.h> + +//-------------------------------------------------------------------- +// TODO copied from OSNetworkSystem. Might get into a separate .h file +/** + * Throws an IOException with the given message. + */ +static void throwSocketException(JNIEnv *env, const char *message) { + jclass exClass = (*env)->FindClass(env, "java/net/SocketException"); + + if(exClass == NULL) { + LOGE("Unable to find class java/net/SocketException"); + } else { + (*env)->ThrowNew(env, exClass, message); + } +} + + +/** + * Throws a NullPointerException. + */ +static void throwNullPointerException(JNIEnv *env) { + jclass exClass = (*env)->FindClass(env, "java/lang/NullPointerException"); + + if(exClass == NULL) { + LOGE("Unable to find class java/lang/NullPointerException"); + } else { + (*env)->ThrowNew(env, exClass, NULL); + } +} + +/** + * @name Socket Errors + * Error codes for socket operations + * + * @internal SOCKERR* range from -200 to -299 avoid overlap + */ +#define SOCKERR_BADSOCKET -200 /* generic error */ +#define SOCKERR_NOTINITIALIZED -201 /* socket library uninitialized */ +#define SOCKERR_BADAF -202 /* bad address family */ +#define SOCKERR_BADPROTO -203 /* bad protocol */ +#define SOCKERR_BADTYPE -204 /* bad type */ +#define SOCKERR_SYSTEMBUSY -205 /* system busy handling requests */ +#define SOCKERR_SYSTEMFULL -206 /* too many sockets */ +#define SOCKERR_NOTCONNECTED -207 /* socket is not connected */ +#define SOCKERR_INTERRUPTED -208 /* the call was cancelled */ +#define SOCKERR_TIMEOUT -209 /* the operation timed out */ +#define SOCKERR_CONNRESET -210 /* the connection was reset */ +#define SOCKERR_WOULDBLOCK -211 /* the socket is marked as nonblocking operation would block */ +#define SOCKERR_ADDRNOTAVAIL -212 /* address not available */ +#define SOCKERR_ADDRINUSE -213 /* address already in use */ +#define SOCKERR_NOTBOUND -214 /* the socket is not bound */ +#define SOCKERR_UNKNOWNSOCKET -215 /* resolution of fileDescriptor to socket failed */ +#define SOCKERR_INVALIDTIMEOUT -216 /* the specified timeout is invalid */ +#define SOCKERR_FDSETFULL -217 /* Unable to create an FDSET */ +#define SOCKERR_TIMEVALFULL -218 /* Unable to create a TIMEVAL */ +#define SOCKERR_REMSOCKSHUTDOWN -219 /* The remote socket has shutdown gracefully */ +#define SOCKERR_NOTLISTENING -220 /* listen() was not invoked prior to accept() */ +#define SOCKERR_NOTSTREAMSOCK -221 /* The socket does not support connection-oriented service */ +#define SOCKERR_ALREADYBOUND -222 /* The socket is already bound to an address */ +#define SOCKERR_NBWITHLINGER -223 /* The socket is marked non-blocking & SO_LINGER is non-zero */ +#define SOCKERR_ISCONNECTED -224 /* The socket is already connected */ +#define SOCKERR_NOBUFFERS -225 /* No buffer space is available */ +#define SOCKERR_HOSTNOTFOUND -226 /* Authoritative Answer Host not found */ +#define SOCKERR_NODATA -227 /* Valid name, no data record of requested type */ +#define SOCKERR_BOUNDORCONN -228 /* The socket has not been bound or is already connected */ +#define SOCKERR_OPNOTSUPP -229 /* The socket does not support the operation */ +#define SOCKERR_OPTUNSUPP -230 /* The socket option is not supported */ +#define SOCKERR_OPTARGSINVALID -231 /* The socket option arguments are invalid */ +#define SOCKERR_SOCKLEVELINVALID -232 /* The socket level is invalid */ +#define SOCKERR_TIMEOUTFAILURE -233 +#define SOCKERR_SOCKADDRALLOCFAIL -234 /* Unable to allocate the sockaddr structure */ +#define SOCKERR_FDSET_SIZEBAD -235 /* The calculated maximum size of the file descriptor set is bad */ +#define SOCKERR_UNKNOWNFLAG -236 /* The flag is unknown */ +#define SOCKERR_MSGSIZE -237 /* The datagram was too big to fit the specified buffer & was truncated. */ +#define SOCKERR_NORECOVERY -238 /* The operation failed with no recovery possible */ +#define SOCKERR_ARGSINVALID -239 /* The arguments are invalid */ +#define SOCKERR_BADDESC -240 /* The socket argument is not a valid file descriptor */ +#define SOCKERR_NOTSOCK -241 /* The socket argument is not a socket */ +#define SOCKERR_HOSTENTALLOCFAIL -242 /* Unable to allocate the hostent structure */ +#define SOCKERR_TIMEVALALLOCFAIL -243 /* Unable to allocate the timeval structure */ +#define SOCKERR_LINGERALLOCFAIL -244 /* Unable to allocate the linger structure */ +#define SOCKERR_IPMREQALLOCFAIL -245 /* Unable to allocate the ipmreq structure */ +#define SOCKERR_FDSETALLOCFAIL -246 /* Unable to allocate the fdset structure */ +#define SOCKERR_OPFAILED -247 +#define SOCKERR_VALUE_NULL -248 /* The value indexed was NULL */ +#define SOCKERR_CONNECTION_REFUSED -249 /* connection was refused */ +#define SOCKERR_ENETUNREACH -250 /* network is not reachable */ +#define SOCKERR_EACCES -251 /* permissions do not allow action on socket */ + +/** + * Answer the errorString corresponding to the errorNumber, if available. + * This function will answer a default error string, if the errorNumber is not + * recognized. + * + * This function will have to be reworked to handle internationalization properly, removing + * the explicit strings. + * + * @param anErrorNum the error code to resolve to a human readable string + * + * @return a human readable error string + */ + +static char * netLookupErrorString(int anErrorNum) { + switch(anErrorNum) { + case SOCKERR_BADSOCKET: + return "Bad socket"; + case SOCKERR_NOTINITIALIZED: + return "Socket library uninitialized"; + case SOCKERR_BADAF: + return "Bad address family"; + case SOCKERR_BADPROTO: + return "Bad protocol"; + case SOCKERR_BADTYPE: + return "Bad type"; + case SOCKERR_SYSTEMBUSY: + return "System busy handling requests"; + case SOCKERR_SYSTEMFULL: + return "Too many sockets allocated"; + case SOCKERR_NOTCONNECTED: + return "Socket is not connected"; + case SOCKERR_INTERRUPTED: + return "The call was cancelled"; + case SOCKERR_TIMEOUT: + return "The operation timed out"; + case SOCKERR_CONNRESET: + return "The connection was reset"; + case SOCKERR_WOULDBLOCK: + return "The socket is marked as nonblocking operation would block"; + case SOCKERR_ADDRNOTAVAIL: + return "The address is not available"; + case SOCKERR_ADDRINUSE: + return "The address is already in use"; + case SOCKERR_NOTBOUND: + return "The socket is not bound"; + case SOCKERR_UNKNOWNSOCKET: + return "Resolution of the FileDescriptor to socket failed"; + case SOCKERR_INVALIDTIMEOUT: + return "The specified timeout is invalid"; + case SOCKERR_FDSETFULL: + return "Unable to create an FDSET"; + case SOCKERR_TIMEVALFULL: + return "Unable to create a TIMEVAL"; + case SOCKERR_REMSOCKSHUTDOWN: + return "The remote socket has shutdown gracefully"; + case SOCKERR_NOTLISTENING: + return "Listen() was not invoked prior to accept()"; + case SOCKERR_NOTSTREAMSOCK: + return "The socket does not support connection-oriented service"; + case SOCKERR_ALREADYBOUND: + return "The socket is already bound to an address"; + case SOCKERR_NBWITHLINGER: + return "The socket is marked non-blocking & SO_LINGER is non-zero"; + case SOCKERR_ISCONNECTED: + return "The socket is already connected"; + case SOCKERR_NOBUFFERS: + return "No buffer space is available"; + case SOCKERR_HOSTNOTFOUND: + return "Authoritative Answer Host not found"; + case SOCKERR_NODATA: + return "Valid name, no data record of requested type"; + case SOCKERR_BOUNDORCONN: + return "The socket has not been bound or is already connected"; + case SOCKERR_OPNOTSUPP: + return "The socket does not support the operation"; + case SOCKERR_OPTUNSUPP: + return "The socket option is not supported"; + case SOCKERR_OPTARGSINVALID: + return "The socket option arguments are invalid"; + case SOCKERR_SOCKLEVELINVALID: + return "The socket level is invalid"; + case SOCKERR_TIMEOUTFAILURE: + return "The timeout operation failed"; + case SOCKERR_SOCKADDRALLOCFAIL: + return "Failed to allocate address structure"; + case SOCKERR_FDSET_SIZEBAD: + return "The calculated maximum size of the file descriptor set is bad"; + case SOCKERR_UNKNOWNFLAG: + return "The flag is unknown"; + case SOCKERR_MSGSIZE: + return "The datagram was too big to fit the specified buffer, so truncated"; + case SOCKERR_NORECOVERY: + return "The operation failed with no recovery possible"; + case SOCKERR_ARGSINVALID: + return "The arguments are invalid"; + case SOCKERR_BADDESC: + return "The socket argument is not a valid file descriptor"; + case SOCKERR_NOTSOCK: + return "The socket argument is not a socket"; + case SOCKERR_HOSTENTALLOCFAIL: + return "Unable to allocate the hostent structure"; + case SOCKERR_TIMEVALALLOCFAIL: + return "Unable to allocate the timeval structure"; + case SOCKERR_LINGERALLOCFAIL: + return "Unable to allocate the linger structure"; + case SOCKERR_IPMREQALLOCFAIL: + return "Unable to allocate the ipmreq structure"; + case SOCKERR_FDSETALLOCFAIL: + return "Unable to allocate the fdset structure"; + case SOCKERR_CONNECTION_REFUSED: + return "Connection refused"; + + default: + return "unkown error"; + } +} + +/** + * Converts a native address structure to a 4-byte array. Throws a + * NullPointerException or an IOException in case of error. This is + * signaled by a return value of -1. The normal return value is 0. + */ +static int structInToJavaAddress( + JNIEnv *env, struct in_addr *address, jbyteArray java_address) { + + if(java_address == NULL) { + throwNullPointerException(env); + return -1; + } + + if((*env)->GetArrayLength(env, java_address) != sizeof(address->s_addr)) { + jniThrowIOException(env, errno); + return -1; + } + + jbyte *java_address_bytes; + + java_address_bytes = (*env)->GetByteArrayElements(env, java_address, NULL); + + memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr)); + + (*env)->ReleaseByteArrayElements(env, java_address, java_address_bytes, 0); + + return 0; +} + +static jobject structInToInetAddress(JNIEnv *env, struct in_addr *address) { + jbyteArray bytes; + int success; + + bytes = (*env)->NewByteArray(env, 4); + + if(bytes == NULL) { + return NULL; + } + + success = structInToJavaAddress(env, address, bytes); + + if(success < 0) { + return NULL; + } + + jclass iaddrclass = (*env)->FindClass(env, "java/net/InetAddress"); + + if(iaddrclass == NULL) { + LOGE("Can't find java/net/InetAddress"); + jniThrowException(env, "java/lang/ClassNotFoundException", "java.net.InetAddress"); + return NULL; + } + + jmethodID iaddrgetbyaddress = (*env)->GetStaticMethodID(env, iaddrclass, "getByAddress", "([B)Ljava/net/InetAddress;"); + + if(iaddrgetbyaddress == NULL) { + LOGE("Can't find method InetAddress.getByAddress(byte[] val)"); + jniThrowException(env, "java/lang/NoSuchMethodError", "InetAddress.getByAddress(byte[] val)"); + return NULL; + } + + return (*env)->CallStaticObjectMethod(env, iaddrclass, iaddrgetbyaddress, bytes); +} +//-------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + +/* structure for returning either and IPV4 or IPV6 ip address */ +typedef struct ipAddress_struct { + union { + char bytes[sizeof(struct in_addr)]; + struct in_addr inAddr; + } addr; + unsigned int length; + unsigned int scope; +} ipAddress_struct; + +/* structure for returning network interface information */ +typedef struct NetworkInterface_struct { + char *name; + char *displayName; + unsigned int numberAddresses; + unsigned int index; + struct ipAddress_struct *addresses; +} NetworkInterface_struct; + +/* array of network interface structures */ +typedef struct NetworkInterfaceArray_struct { + unsigned int length; + struct NetworkInterface_struct *elements; +} NetworkInterfaceArray_struct; + + + + + + + + + + + + + + + + + + + + + + + + + + + +/** + * Frees the memory allocated for the hyNetworkInterface_struct array passed in + * + * @param[in] portLibrary The port library. + * @param[in] handle Pointer to array of network interface structures to be freed + * + * @return 0 on success +*/ +int sock_free_network_interface_struct (struct NetworkInterfaceArray_struct *array) { + unsigned int i = 0; + + if((array != NULL) && (array->elements != NULL)) { + + /* free the allocated memory in each of the structures */ + for(i = 0; i < array->length; i++) { + + /* free the name, displayName and addresses */ + if(array->elements[i].name != NULL) { + free(array->elements[i].name); + } + + if(array->elements[i].displayName != NULL) { + free(array->elements[i].displayName); + } + + if(array->elements[i].addresses != NULL) { + free(array->elements[i].addresses); + } + } + + /* now free the array itself */ + free(array->elements); + } + + return 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/** + * Queries and returns the information for the network interfaces that are currently active within the system. + * Applications are responsible for freeing the memory returned via the handle. + * + * @param[in] portLibrary The port library. + * @param[in,out] array Pointer to structure with array of network interface entries + * @param[in] boolean which indicates if we should prefer the IPv4 stack or not + * + * @return The number of elements in handle on success, negatvie portable error code on failure. + -WSANO_RECOVERY if system calls required to get the info fail, -WSAENOBUFS if memory allocation fails + * @note A return value of 0 indicates no interfaces exist +*/ +int sockGetNetworkInterfaces(struct NetworkInterfaceArray_struct * array) { + + struct NetworkInterface_struct *interfaces = NULL; + unsigned int nameLength = 0; + unsigned int currentAdapterIndex = 0; + unsigned int counter = 0; + unsigned int result = 0; + unsigned int numAddresses = 0; + unsigned int currentIPAddressIndex = 0; + unsigned int numAdapters = 0; + int err = 0; + + struct ifconf ifc; + int len = 32 * sizeof(struct ifreq); + int socketP = 0; + unsigned int totalInterfaces = 0; + struct ifreq reqCopy; + unsigned int counter2 = 0; + char *lastName = NULL; + + int ifconfCommand = SIOCGIFCONF; + + /* this method is not guarranteed to return the IPV6 addresses. Code is include so that if the platform returns IPV6 addresses + in reply to the SIOCGIFCONF they will be included. Howerver, it is not guarranteed or even expected that many platforms will + include the IPV6 addresses. For this reason there are other specific implementations that will return the IPV6 addresses */ + /* first get the list of interfaces. We do not know how long the buffer needs to be so we try with one that allows for + 32 interfaces. If this turns out not to be big enough then we expand the buffer to be able to support another + 32 interfaces and try again. We do this until the result indicates that the result fit into the buffer provided */ + /* we need socket to do the ioctl so create one */ + socketP = socket(PF_INET, SOCK_DGRAM, 0); + if(socketP < 0) { + return socketP; + } + for(;;) { + char *data = (char *)malloc(len * sizeof(char)); + if(data == NULL) { + close(socketP); + return SOCKERR_NOBUFFERS; + } + ifc.ifc_len = len; + ifc.ifc_buf = data; + errno = 0; + if(ioctl(socketP, ifconfCommand, &ifc) != 0) { + err = errno; + free(ifc.ifc_buf); + close(socketP); + return SOCKERR_NORECOVERY; + } + if(ifc.ifc_len < len) + break; + /* the returned data was likely truncated, expand the buffer and try again */ + free(ifc.ifc_buf); + len += 32 * sizeof(struct ifreq); + } + + /* get the number of distinct interfaces */ + if(ifc.ifc_len != 0) { + totalInterfaces = ifc.ifc_len / sizeof(struct ifreq); + } + lastName = NULL; + for(counter = 0; counter < totalInterfaces; counter++) { + if((NULL == lastName) || (strncmp(lastName, ifc.ifc_req[counter].ifr_name, IFNAMSIZ) != 0)) { + /* make sure the interface is up */ + reqCopy = ifc.ifc_req[counter]; + ioctl(socketP, SIOCGIFFLAGS, &reqCopy); + if((reqCopy.ifr_flags) & (IFF_UP == IFF_UP)) { + numAdapters++; + } + } + lastName = ifc.ifc_req[counter].ifr_name; + } + + /* now allocate the space for the hyNetworkInterface structs and fill it in */ + interfaces = malloc(numAdapters * sizeof(NetworkInterface_struct)); + if(NULL == interfaces) { + free(ifc.ifc_buf); + close(socketP); + return SOCKERR_NOBUFFERS; + } + + /* initialize the structure so that we can free allocated if a failure occurs */ + for(counter = 0; counter < numAdapters; counter++) { + interfaces[counter].name = NULL; + interfaces[counter].displayName = NULL; + interfaces[counter].addresses = NULL; + } + + /* set up the return stucture */ + array->elements = interfaces; + array->length = numAdapters; + lastName = NULL; + for(counter = 0; counter < totalInterfaces; counter++) { + /* make sure the interface is still up */ + reqCopy = ifc.ifc_req[counter]; + ioctl(socketP, SIOCGIFFLAGS, &reqCopy); + if((reqCopy.ifr_flags) & (IFF_UP == IFF_UP)) { + /* since this function can return multiple entries for the same name, only do it for the first one with any given name */ + if((NULL == lastName) || (strncmp(lastName, ifc.ifc_req[counter].ifr_name, IFNAMSIZ) != 0)) { + + /* get the index for the interface. This is only truely necessary on platforms that support IPV6 */ + interfaces[currentAdapterIndex].index = 0; + /* get the name and display name for the adapter */ + /* there only seems to be one name so use it for both the name and the display name */ + nameLength = strlen(ifc.ifc_req[counter].ifr_name); + interfaces[currentAdapterIndex].name = malloc(nameLength + 1); + + if(NULL == interfaces[currentAdapterIndex].name) { + free(ifc.ifc_buf); + sock_free_network_interface_struct(array); + close(socketP); + return SOCKERR_NOBUFFERS; + } + strncpy(interfaces[currentAdapterIndex].name, ifc.ifc_req[counter].ifr_name, nameLength); + interfaces[currentAdapterIndex].name[nameLength] = 0; + nameLength = strlen(ifc.ifc_req[counter].ifr_name); + interfaces[currentAdapterIndex].displayName = malloc(nameLength + 1); + if(NULL == interfaces[currentAdapterIndex].displayName) { + free(ifc.ifc_buf); + sock_free_network_interface_struct(array); + close(socketP); + return SOCKERR_NOBUFFERS; + } + strncpy(interfaces[currentAdapterIndex].displayName, ifc.ifc_req[counter].ifr_name, nameLength); + interfaces[currentAdapterIndex].displayName[nameLength] = 0; + + /* check how many addresses/aliases this adapter has. aliases show up as adaptors with the same name */ + numAddresses = 0; + for(counter2 = counter; counter2 < totalInterfaces; counter2++) { + if(strncmp(ifc.ifc_req[counter].ifr_name, ifc.ifc_req[counter2].ifr_name, IFNAMSIZ) == 0) { + if(ifc.ifc_req[counter2].ifr_addr.sa_family == AF_INET) { + numAddresses++; + } + } else { + break; + } + } + + /* allocate space for the addresses */ + interfaces[currentAdapterIndex].numberAddresses = numAddresses; + interfaces[currentAdapterIndex].addresses = malloc(numAddresses * sizeof(ipAddress_struct)); + if(NULL == interfaces[currentAdapterIndex].addresses) { + free(ifc.ifc_buf); + sock_free_network_interface_struct(array); + close(socketP); + return SOCKERR_NOBUFFERS; + } + + /* now get the addresses */ + currentIPAddressIndex = 0; + lastName = ifc.ifc_req[counter].ifr_name; + + for(;;) { + if(ifc.ifc_req[counter].ifr_addr.sa_family == AF_INET) { + interfaces[currentAdapterIndex].addresses[currentIPAddressIndex].addr.inAddr.s_addr = ((struct sockaddr_in *) (&ifc.ifc_req[counter].ifr_addr))->sin_addr.s_addr; + interfaces[currentAdapterIndex].addresses[currentIPAddressIndex].length = sizeof(struct in_addr); + interfaces[currentAdapterIndex].addresses[currentIPAddressIndex].scope = 0; + currentIPAddressIndex++; + } + + /* we mean to increment the outside counter here as we want to skip the next entry as it is for the same interface + as we are currently working on */ + if((counter + 1 < totalInterfaces) && (strncmp(ifc.ifc_req[counter + 1].ifr_name, lastName, IFNAMSIZ) == 0)) { + counter++; + } else { + break; + } + + } + currentAdapterIndex++; + } + } + } /* for over all interfaces */ + /* now an interface might have been taken down since we first counted them */ + array->length = currentAdapterIndex; + /* free the memory now that we are done with it */ + free(ifc.ifc_buf); + close(socketP); + + return 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/** + * Answer an array of NetworkInterface objects. One for each network interface within the system + * + * @param env pointer to the JNI library + * @param clazz the class of the object invoking the JNI function + * + * @return an array of NetworkInterface objects of length 0 or more + */ + +static jobjectArray getNetworkInterfacesImpl(JNIEnv * env, jclass clazz) { + + /* variables to store network interfac edata returned by call to port library */ + struct NetworkInterfaceArray_struct networkInterfaceArray; + int result = 0; + + /* variables for class and method objects needed to create bridge to java */ + jclass networkInterfaceClass = NULL; + jclass inetAddressClass = NULL; + jclass utilClass = NULL; + jmethodID methodID = NULL; + jmethodID utilMid = NULL; + + /* JNI objects used to return values from native call */ + jstring name = NULL; + jstring displayName = NULL; + jobjectArray addresses = NULL; + jobjectArray networkInterfaces = NULL; + jbyteArray bytearray = NULL; + + /* jobjects used to build the object arrays returned */ + jobject currentInterface = NULL; + jobject element = NULL; + + /* misc variables needed for looping and determining inetAddress info */ + unsigned int i = 0; + unsigned int j = 0; + unsigned int nameLength = 0; + + /* get the classes and methods that we need for later calls */ + networkInterfaceClass = (*env)->FindClass(env, "java/net/NetworkInterface"); + if(networkInterfaceClass == NULL) { + throwSocketException(env, netLookupErrorString(SOCKERR_NORECOVERY)); + return NULL; + } + + inetAddressClass = (*env)->FindClass(env, "java/net/InetAddress"); + if(inetAddressClass == NULL) { + throwSocketException(env, netLookupErrorString(SOCKERR_NORECOVERY)); + return NULL; + } + + methodID = (*env)->GetMethodID(env, networkInterfaceClass, "<init>", + "(Ljava/lang/String;Ljava/lang/String;[Ljava/net/InetAddress;I)V"); + if(methodID == NULL) { + throwSocketException(env, netLookupErrorString(SOCKERR_NORECOVERY)); + return NULL; + } + + utilClass = (*env)->FindClass(env, "org/apache/harmony/luni/util/Util"); + if(!utilClass) { + return NULL; + } + + utilMid = ((*env)->GetStaticMethodID(env, utilClass, "toString", + "([BII)Ljava/lang/String;")); + if(!utilMid) { + return NULL; + } + + result = sockGetNetworkInterfaces(&networkInterfaceArray); + + if(result < 0) { + /* this means an error occured. The value returned is the socket error that should be returned */ + throwSocketException(env, netLookupErrorString(result)); + return NULL; + } + + /* now loop through the interfaces and extract the information to be returned */ + for(j = 0; j < networkInterfaceArray.length; j++) { + /* set the name and display name and reset the addresses object array */ + addresses = NULL; + name = NULL; + displayName = NULL; + + if(networkInterfaceArray.elements[j].name != NULL) { + nameLength = strlen(networkInterfaceArray.elements[j].name); + bytearray = (*env)->NewByteArray(env, nameLength); + if(bytearray == NULL) { + /* NewByteArray should have thrown an exception */ + return NULL; + } + (*env)->SetByteArrayRegion(env, bytearray, (jint) 0, nameLength, + (jbyte *)networkInterfaceArray.elements[j].name); + name = (*env)->CallStaticObjectMethod(env, utilClass, utilMid, + bytearray, (jint) 0, nameLength); + if((*env)->ExceptionCheck(env)) { + return NULL; + } + } + + if(networkInterfaceArray.elements[j].displayName != NULL) { + nameLength = strlen(networkInterfaceArray.elements[j].displayName); + bytearray = (*env)->NewByteArray(env, nameLength); + if(bytearray == NULL) { + /* NewByteArray should have thrown an exception */ + return NULL; + } + (*env)->SetByteArrayRegion(env, bytearray, (jint) 0, nameLength, + (jbyte *)networkInterfaceArray.elements[j].displayName); + displayName = (*env)->CallStaticObjectMethod(env, utilClass, utilMid, + bytearray, (jint) 0, nameLength); + if((*env)->ExceptionCheck(env)) { + return NULL; + } + } + + /* generate the object with the inet addresses for the itnerface */ + for(i = 0; i < networkInterfaceArray.elements[j].numberAddresses; i++) { + element = structInToInetAddress(env, (struct in_addr *) &(networkInterfaceArray.elements[j].addresses[i].addr.inAddr)); + if(i == 0) { + addresses = (*env)->NewObjectArray(env, + networkInterfaceArray.elements[j].numberAddresses, + inetAddressClass, element); + } else { + (*env)->SetObjectArrayElement(env, addresses, i, element); + } + } + + /* now create the NetworkInterface object for this interface and then add it it ot the arrary that will be returned */ + currentInterface = (*env)->NewObject(env, networkInterfaceClass, + methodID, name, displayName, addresses, + networkInterfaceArray.elements[j].index); + + if(j == 0) { + networkInterfaces = (*env)->NewObjectArray(env, + networkInterfaceArray.length, networkInterfaceClass, + currentInterface); + } else { + (*env)->SetObjectArrayElement(env, networkInterfaces, j, currentInterface); + } + } + + /* free the memory for the interfaces struct and return the new NetworkInterface List */ + sock_free_network_interface_struct(&networkInterfaceArray); + return networkInterfaces; +} + + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "getNetworkInterfacesImpl", "()[Ljava/net/NetworkInterface;", getNetworkInterfacesImpl } +}; +int register_java_net_NetworkInterface(JNIEnv* env) { + return jniRegisterNativeMethods(env, "java/net/NetworkInterface", + gMethods, NELEM(gMethods)); + +} diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp new file mode 100644 index 0000000..38f3d36 --- /dev/null +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp @@ -0,0 +1,807 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Common natives supporting the file system interface. + */ + +#define HyMaxPath 1024 +#define HyOpenRead 1 /* Values for HyFileOpen */ +#define HyOpenWrite 2 +#define HyOpenCreate 4 +#define HyOpenTruncate 8 +#define HyOpenAppend 16 +#define HyOpenText 32 + +/* Use this flag with HyOpenCreate, if this flag is specified then + * trying to create an existing file will fail + */ +#define HyOpenCreateNew 64 +#define HyOpenSync 128 +#define SHARED_LOCK_TYPE 1L + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/sendfile.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +typedef struct socket_struct { + int sock; + unsigned short family; +} socket_struct; + +static void convertToPlatform(char *path) { + char *pathIndex; + + pathIndex = path; + while (*pathIndex != '\0') { + if(*pathIndex == '\\') { + *pathIndex = '/'; + } + pathIndex++; + } +} + +static int +EsTranslateOpenFlags(int flags) { + int realFlags = 0; + + if(flags & HyOpenAppend) { + realFlags |= O_APPEND; + } + if(flags & HyOpenTruncate) { + realFlags |= O_TRUNC; + } + if(flags & HyOpenCreate) { + realFlags |= O_CREAT; + } + if(flags & HyOpenCreateNew) { + realFlags |= O_EXCL | O_CREAT; + } +#ifdef O_SYNC + if(flags & HyOpenSync) { + realFlags |= O_SYNC; + } +#endif + if(flags & HyOpenRead) { + if(flags & HyOpenWrite) { + return (O_RDWR | realFlags); + } + return (O_RDONLY | realFlags); + } + if(flags & HyOpenWrite) { + return (O_WRONLY | realFlags); + } + return -1; +} + +/** + * Lock the file identified by the given handle. + * The range and lock type are given. + */ +static jint harmony_io_lockImpl(JNIEnv * env, jobject thiz, jint handle, + jlong start, jlong length, jint typeFlag, jboolean waitFlag) { + + int rc; + int waitMode = (waitFlag) ? F_SETLKW : F_SETLK; + struct flock lock; + + memset(&lock, 0, sizeof(lock)); + + // If start or length overflow the max values we can represent, then max them out. + if(start > 0x7fffffffL) { + start = 0x7fffffffL; + } + if(length > 0x7fffffffL) { + length = 0x7fffffffL; + } + + lock.l_whence = SEEK_SET; + lock.l_start = start; + lock.l_len = length; + + if((typeFlag & SHARED_LOCK_TYPE) == SHARED_LOCK_TYPE) { + lock.l_type = F_RDLCK; + } else { + lock.l_type = F_WRLCK; + } + + do { + rc = fcntl(handle, waitMode, &lock); + } while ((rc < 0) && (errno == EINTR)); + + return (rc == -1) ? -1 : 0; +} + +/** + * Unlocks the specified region of the file. + */ +static jint harmony_io_unlockImpl(JNIEnv * env, jobject thiz, jint handle, + jlong start, jlong length) { + + int rc; + struct flock lock; + + memset(&lock, 0, sizeof(lock)); + + // If start or length overflow the max values we can represent, then max them out. + if(start > 0x7fffffffL) { + start = 0x7fffffffL; + } + if(length > 0x7fffffffL) { + length = 0x7fffffffL; + } + + lock.l_whence = SEEK_SET; + lock.l_start = start; + lock.l_len = length; + lock.l_type = F_UNLCK; + + do { + rc = fcntl(handle, F_SETLKW, &lock); + } while ((rc < 0) && (errno == EINTR)); + + return (rc == -1) ? -1 : 0; +} + +/** + * Returns the granularity of the starting address for virtual memory allocation. + * (It's the same as the page size.) + * Class: org_apache_harmony_luni_platform_OSFileSystem + * Method: getAllocGranularity + * Signature: ()I + */ +static jint harmony_io_getAllocGranularity(JNIEnv * env, jobject thiz) { + static int allocGranularity = 0; + if(allocGranularity == 0) { + allocGranularity = getpagesize(); + } + return allocGranularity; +} + +/* + * Class: org_apache_harmony_luni_platform_OSFileSystem + * Method: readvImpl + * Signature: (I[J[I[I)J + */ +static jlong harmony_io_readvImpl(JNIEnv *env, jobject thiz, jint fd, + jintArray jbuffers, jintArray joffsets, jintArray jlengths, jint size) { + + jboolean bufsCopied = JNI_FALSE; + jboolean offsetsCopied = JNI_FALSE; + jboolean lengthsCopied = JNI_FALSE; + jint *bufs; + jint *offsets; + jint *lengths; + int i = 0; + long totalRead = 0; + struct iovec *vectors = (struct iovec *)malloc(size * sizeof(struct iovec)); + if(vectors == NULL) { + return -1; + } + bufs = env->GetIntArrayElements(jbuffers, &bufsCopied); + offsets = env->GetIntArrayElements(joffsets, &offsetsCopied); + lengths = env->GetIntArrayElements(jlengths, &lengthsCopied); + while(i < size) { + vectors[i].iov_base = (void *)((int)(bufs[i]+offsets[i])); + vectors[i].iov_len = lengths[i]; + i++; + } + totalRead = readv(fd, vectors, size); + if(bufsCopied) { + env->ReleaseIntArrayElements(jbuffers, bufs, JNI_ABORT); + } + if(offsetsCopied) { + env->ReleaseIntArrayElements(joffsets, offsets, JNI_ABORT); + } + if(lengthsCopied) { + env->ReleaseIntArrayElements(jlengths, lengths, JNI_ABORT); + } + free(vectors); + return totalRead; +} + +/* + * Class: org_apache_harmony_luni_platform_OSFileSystem + * Method: writevImpl + * Signature: (I[J[I[I)J + */ +static jlong harmony_io_writevImpl(JNIEnv *env, jobject thiz, jint fd, + jintArray jbuffers, jintArray joffsets, jintArray jlengths, jint size) { + + jboolean bufsCopied = JNI_FALSE; + jboolean offsetsCopied = JNI_FALSE; + jboolean lengthsCopied = JNI_FALSE; + jint *bufs; + jint *offsets; + jint *lengths; + int i = 0; + long totalRead = 0; + struct iovec *vectors = (struct iovec *)malloc(size * sizeof(struct iovec)); + if(vectors == NULL) { + return -1; + } + bufs = env->GetIntArrayElements(jbuffers, &bufsCopied); + offsets = env->GetIntArrayElements(joffsets, &offsetsCopied); + lengths = env->GetIntArrayElements(jlengths, &lengthsCopied); + while(i < size) { + vectors[i].iov_base = (void *)((int)(bufs[i]+offsets[i])); + vectors[i].iov_len = lengths[i]; + i++; + } + totalRead = writev(fd, vectors, size); + if(bufsCopied) { + env->ReleaseIntArrayElements(jbuffers, bufs, JNI_ABORT); + } + if(offsetsCopied) { + env->ReleaseIntArrayElements(joffsets, offsets, JNI_ABORT); + } + if(lengthsCopied) { + env->ReleaseIntArrayElements(jlengths, lengths, JNI_ABORT); + } + free(vectors); + return totalRead; +} + +/* + * Class: org_apache_harmony_luni_platform_OSFileSystem + * Method: transferImpl + * Signature: (IJJ)J + */ +static jlong harmony_io_transferImpl(JNIEnv *env, jobject thiz, jint fd, + jobject sd, jlong offset, jlong count) { + + int socket; + off_t off; + + socket = jniGetFDFromFileDescriptor(env, sd); + if(socket == 0 || socket == -1) { + return -1; + } + + /* Value of offset is checked in jint scope (checked in java layer) + The conversion here is to guarantee no value lost when converting offset to off_t + */ + off = offset; + + return sendfile(socket,(int)fd,(off_t *)&off,(size_t)count); +} + +/* + * Class: org_apache_harmony_io + * Method: readDirectImpl + * Signature: (IJI)J + */ +static jlong harmony_io_readDirectImpl(JNIEnv * env, jobject thiz, jint fd, + jint buf, jint offset, jint nbytes) { + jint result; + if(nbytes == 0) { + return (jlong) 0; + } + + result = read(fd, (void *) ((jint *)(buf+offset)), (int) nbytes); + if(result == 0) { + return (jlong) -1; + } else { + return (jlong) result; + } +} + +/* + * Class: org_apache_harmony_io + * Method: writeDirectImpl + * Signature: (IJI)J + */ +static jlong harmony_io_writeDirectImpl(JNIEnv * env, jobject thiz, jint fd, + jint buf, jint offset, jint nbytes) { + + + int rc = 0; + + /* write will just do the right thing for HYPORT_TTY_OUT and HYPORT_TTY_ERR */ + rc = write (fd, (const void *) ((jint *)(buf+offset)), (int) nbytes); + + if(rc == -1) { + jniThrowException(env, "java/io/IOException", strerror(errno)); + return -2; + } + return (jlong) rc; + +} + +// BEGIN android-changed +/* + * Class: org_apache_harmony_io + * Method: readImpl + * Signature: (I[BII)J + */ +static jlong harmony_io_readImpl(JNIEnv * env, jobject thiz, jint fd, + jbyteArray byteArray, jint offset, jint nbytes) { + + jboolean isCopy; + jbyte *bytes; + jlong result; + + if (nbytes == 0) { + return 0; + } + + bytes = env->GetByteArrayElements(byteArray, &isCopy); + + for (;;) { + result = read(fd, (void *) (bytes + offset), (int) nbytes); + + if ((result != -1) || (errno != EINTR)) { + break; + } + + /* + * If we didn't break above, that means that the read() call + * returned due to EINTR. We shield Java code from this + * possibility by trying again. Note that this is different + * from EAGAIN, which should result in this code throwing + * an InterruptedIOException. + */ + } + + env->ReleaseByteArrayElements(byteArray, bytes, 0); + + if (result == 0) { + return -1; + } + + if (result == -1) { + if (errno == EAGAIN) { + jniThrowException(env, "java/io/InterruptedIOException", + "Read timed out"); + } else { + jniThrowException(env, "java/io/IOException", strerror(errno)); + } + } + + return result; +} + +/* + * Class: org_apache_harmony_io + * Method: writeImpl + * Signature: (I[BII)J + */ +static jlong harmony_io_writeImpl(JNIEnv * env, jobject thiz, jint fd, + jbyteArray byteArray, jint offset, jint nbytes) { + + jboolean isCopy; + jbyte *bytes = env->GetByteArrayElements(byteArray, &isCopy); + jlong result; + + for (;;) { + result = write(fd, (const char *) bytes + offset, (int) nbytes); + + if ((result != -1) || (errno != EINTR)) { + break; + } + + /* + * If we didn't break above, that means that the read() call + * returned due to EINTR. We shield Java code from this + * possibility by trying again. Note that this is different + * from EAGAIN, which should result in this code throwing + * an InterruptedIOException. + */ + } + + env->ReleaseByteArrayElements(byteArray, bytes, JNI_ABORT); + + if (result == -1) { + if (errno == EAGAIN) { + jniThrowException(env, "java/io/InterruptedIOException", + "Write timed out"); + } else { + jniThrowException(env, "java/io/IOException", strerror(errno)); + } + } + + return result; +} +// END android-changed + +/** + * Seeks a file descriptor to a given file position. + * + * @param env pointer to Java environment + * @param thiz pointer to object receiving the message + * @param fd handle of file to be seeked + * @param offset distance of movement in bytes relative to whence arg + * @param whence enum value indicating from where the offset is relative + * The valid values are defined in fsconstants.h. + * @return the new file position from the beginning of the file, in bytes; + * or -1 if a problem occurs. + */ +static jlong harmony_io_seekImpl(JNIEnv * env, jobject thiz, jint fd, + jlong offset, jint whence) { + + int mywhence = 0; + + /* Convert whence argument */ + switch (whence) { + case 1: + mywhence = 0; + break; + case 2: + mywhence = 1; + break; + case 4: + mywhence = 2; + break; + default: + return -1; + } + + + off_t localOffset = (int) offset; + + if((mywhence < 0) || (mywhence > 2)) { + return -1; + } + + /* If file offsets are 32 bit, truncate the seek to that range */ + if(sizeof (off_t) < sizeof (jlong)) { + if(offset > 0x7FFFFFFF) { + localOffset = 0x7FFFFFFF; + } else if(offset < -0x7FFFFFFF) { + localOffset = -0x7FFFFFFF; + } + } + + return (jlong) lseek(fd, localOffset, mywhence); +} + +/** + * Flushes a file state to disk. + * + * @param env pointer to Java environment + * @param thiz pointer to object receiving the message + * @param fd handle of file to be flushed + * @param metadata if true also flush metadata, + * otherwise just flush data is possible. + * @return zero on success and -1 on failure + * + * Method: fflushImpl + * Signature: (IZ)I + */ +static jint harmony_io_fflushImpl(JNIEnv * env, jobject thiz, jint fd, + jboolean metadata) { + return (jint) fsync(fd); +} + +// BEGIN android-changed +/** + * Closes the given file handle + * + * @param env pointer to Java environment + * @param thiz pointer to object receiving the message + * @param fd handle of file to be closed + * @return zero on success and -1 on failure + * + * Class: org_apache_harmony_io + * Method: closeImpl + * Signature: (I)I + */ +static jint harmony_io_closeImpl(JNIEnv * env, jobject thiz, jint fd) { + jint result; + + for (;;) { + result = (jint) close(fd); + + if ((result != -1) || (errno != EINTR)) { + break; + } + + /* + * If we didn't break above, that means that the close() call + * returned due to EINTR. We shield Java code from this + * possibility by trying again. + */ + } + + return result; +} +// END android-changed + + +/* + * Class: org_apache_harmony_io + * Method: truncateImpl + * Signature: (IJ)I + */ +static jint harmony_io_truncateImpl(JNIEnv * env, jobject thiz, jint fd, + jlong size) { + + int rc; + off_t length = (off_t) size; + + // If file offsets are 32 bit, truncate the newLength to that range + if(sizeof (off_t) < sizeof (jlong)) { + if(length > 0x7FFFFFFF) { + length = 0x7FFFFFFF; + } else if(length < -0x7FFFFFFF) { + length = -0x7FFFFFFF; + } + } + + rc = ftruncate((int)fd, length); + + return (jint) rc; + +} + +/* + * Class: org_apache_harmony_io + * Method: openImpl + * Signature: ([BI)I + */ +static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path, + jint jflags) { + + int flags = 0; + int mode = 0; + jint * portFD; + jsize length; + char pathCopy[HyMaxPath]; + +// BEGIN android-changed +// don't want default permissions to allow global access. + switch(jflags) { + case 0: + flags = HyOpenRead; + mode = 0; + break; + case 1: + flags = HyOpenCreate | HyOpenWrite | HyOpenTruncate; + mode = 0600; + break; + case 16: + flags = HyOpenRead | HyOpenWrite | HyOpenCreate; + mode = 0600; + break; + case 32: + flags = HyOpenRead | HyOpenWrite | HyOpenCreate | HyOpenSync; + mode = 0600; + break; + case 256: + flags = HyOpenWrite | HyOpenCreate | HyOpenAppend; + mode = 0600; + break; + } +// BEGIN android-changed + + flags = EsTranslateOpenFlags(flags); + + length = env->GetArrayLength (path); + length = length < HyMaxPath - 1 ? length : HyMaxPath - 1; + env->GetByteArrayRegion (path, 0, length, (jbyte *)pathCopy); + pathCopy[length] = '\0'; + convertToPlatform (pathCopy); + + int cc; + + if(pathCopy == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return -1; + } + + do { + cc = open(pathCopy, flags, mode); + } while(cc < 0 && errno == EINTR); + + if(cc < 0 && errno > 0) { + cc = -errno; + } + + return cc; + + +} + +// BEGIN android-deleted +#if 0 +/* + * Answers the number of remaining chars in the stdin. + * + * Class: org_apache_harmony_io + * Method: ttyAvailableImpl + * Signature: ()J + */ +static jlong harmony_io_ttyAvailableImpl(JNIEnv *env, jobject thiz) { + + int rc; + off_t curr, end; + + int avail = 0; + + // when redirected from a file + curr = lseek(STDIN_FILENO, 0L, 2); /* don't use tell(), it doesn't exist on all platforms, i.e. linux */ + if(curr != -1) { + end = lseek(STDIN_FILENO, 0L, 4); + lseek(STDIN_FILENO, curr, 1); + if(end >= curr) { + return (jlong) (end - curr); + } + } + + /* ioctl doesn't work for files on all platforms (i.e. SOLARIS) */ + + rc = ioctl (STDIN_FILENO, FIONREAD, &avail); + + /* 64 bit platforms use a 32 bit value, using IDATA fails on big endian */ + /* Pass in IDATA because ioctl() is device dependent, some devices may write 64 bits */ + if(rc != -1) { + return (jlong) *(jint *) & avail; + } + return (jlong) 0; +} +#endif +// END android-deleted + +// BEGIN android-added +/* + * Answers the number of remaining bytes in a file descriptor + * using IOCTL. + * + * Class: org_apache_harmony_io + * Method: ioctlAvailable + * Signature: ()I + */ +static jint harmony_io_ioctlAvailable(JNIEnv *env, jobject thiz, jint fd) { + int avail = 0; + int rc = ioctl(fd, FIONREAD, &avail); + + /* + * On underlying platforms Android cares about (read "Linux"), + * ioctl(fd, FIONREAD, &avail) is supposed to do the following: + * + * If the fd refers to a regular file, avail is set to + * the difference between the file size and the current cursor. + * This may be negative if the cursor is past the end of the file. + * + * If the fd refers to an open socket or the read end of a + * pipe, then avail will be set to a number of bytes that are + * available to be read without blocking. + * + * If the fd refers to a special file/device that has some concept + * of buffering, then avail will be set in a corresponding way. + * + * If the fd refers to a special device that does not have any + * concept of buffering, then the ioctl call will return a negative + * number, and errno will be set to ENOTTY. + * + * If the fd refers to a special file masquerading as a regular file, + * then avail may be returned as negative, in that the special file + * may appear to have zero size and yet a previous read call may have + * actually read some amount of data and caused the cursor to be + * advanced. + */ + + if (rc >= 0) { + /* + * Success, but make sure not to return a negative number (see + * above). + */ + if (avail < 0) { + avail = 0; + } + } else if (errno == ENOTTY) { + /* The fd is unwilling to opine about its read buffer. */ + avail = 0; + } else { + /* Something strange is happening. */ + jniThrowException(env, "java/io/IOException", strerror(errno)); + avail = 0; + } + + return (jint) avail; +} +// END android-added + +/* + * Reads the number of bytes from stdin. + * + * Class: org_apache_harmony_io + * Method: ttyReadImpl + * Signature: ([BII)J + */ +static jlong harmony_io_ttyReadImpl(JNIEnv *env, jobject thiz, + jbyteArray byteArray, jint offset, jint nbytes) { + + jboolean isCopy; + jbyte *bytes = env->GetByteArrayElements(byteArray, &isCopy); + jlong result; + + for(;;) { + + result = (jlong) read(STDIN_FILENO, (char *)(bytes + offset), (int) nbytes); + + if ((result != -1) || (errno != EINTR)) { + break; + } + + /* + * If we didn't break above, that means that the read() call + * returned due to EINTR. We shield Java code from this + * possibility by trying again. Note that this is different + * from EAGAIN, which should result in this code throwing + * an InterruptedIOException. + */ + } + + env->ReleaseByteArrayElements(byteArray, bytes, 0); + + if (result == 0) { + return -1; + } + + if (result == -1) { + if (errno == EAGAIN) { + jniThrowException(env, "java/io/InterruptedIOException", + "Read timed out"); + } else { + jniThrowException(env, "java/io/IOException", strerror(errno)); + } + } + + return result; +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "lockImpl", "(IJJIZ)I", (void*) harmony_io_lockImpl }, + { "getAllocGranularity","()I", (void*) harmony_io_getAllocGranularity }, + { "unlockImpl", "(IJJ)I", (void*) harmony_io_unlockImpl }, + { "fflushImpl", "(IZ)I", (void*) harmony_io_fflushImpl }, + { "seekImpl", "(IJI)J", (void*) harmony_io_seekImpl }, + { "readDirectImpl", "(IIII)J", (void*) harmony_io_readDirectImpl }, + { "writeDirectImpl", "(IIII)J", (void*) harmony_io_writeDirectImpl }, + { "readImpl", "(I[BII)J", (void*) harmony_io_readImpl }, + { "writeImpl", "(I[BII)J", (void*) harmony_io_writeImpl }, + { "readvImpl", "(I[I[I[II)J",(void*) harmony_io_readvImpl }, + { "writevImpl", "(I[I[I[II)J",(void*) harmony_io_writevImpl }, + { "closeImpl", "(I)I", (void*) harmony_io_closeImpl }, + { "truncateImpl", "(IJ)I", (void*) harmony_io_truncateImpl }, + { "openImpl", "([BI)I", (void*) harmony_io_openImpl }, + { "transferImpl", "(ILjava/io/FileDescriptor;JJ)J", + (void*) harmony_io_transferImpl }, + // BEGIN android-deleted + //{ "ttyAvailableImpl", "()J", (void*) harmony_io_ttyAvailableImpl }, + // END android-deleted + // BEGIN android-added + { "ioctlAvailable", "(I)I", (void*) harmony_io_ioctlAvailable }, + // END android added + { "ttyReadImpl", "([BII)J", (void*) harmony_io_ttyReadImpl } +}; +int register_org_apache_harmony_luni_platform_OSFileSystem(JNIEnv *_env) { + return jniRegisterNativeMethods(_env, + "org/apache/harmony/luni/platform/OSFileSystem", gMethods, + NELEM(gMethods)); +} diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp new file mode 100644 index 0000000..5bd7907 --- /dev/null +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp @@ -0,0 +1,582 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JNIHelp.h" +#include "AndroidSystemNatives.h" +#include "utils/misc.h" +#include <sys/mman.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#undef MMAP_READ_ONLY +#define MMAP_READ_ONLY 1L +#undef MMAP_READ_WRITE +#define MMAP_READ_WRITE 2L +#undef MMAP_WRITE_COPY +#define MMAP_WRITE_COPY 4L + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: littleEndian + * Signature: ()Z + */ +static jboolean harmony_nio_littleEndian(JNIEnv *_env, jclass _this) { + long l = 0x01020304; + unsigned char* c = (unsigned char*)&l; + return (*c == 0x04) ? JNI_TRUE : JNI_FALSE; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getPointerSizeImpl + * Signature: ()I + */ +static jint harmony_nio_getPointerSizeImpl(JNIEnv *_env, jclass _this) { + return sizeof(void *); +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: mallocImpl + * Signature: (I)I + */ +static jint harmony_nio_mallocImpl(JNIEnv *_env, jobject _this, jint size) { + void *returnValue = malloc(size); + if(returnValue == NULL) { + jniThrowException(_env, "java.lang.OutOfMemoryError", ""); + } + return (jint)returnValue; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: freeImpl + * Signature: (I)V + */ +static void harmony_nio_freeImpl(JNIEnv *_env, jobject _this, jint pointer) { + free((void *)pointer); +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: memset + * Signature: (IBJ)V + */ +static void harmony_nio_memset(JNIEnv *_env, jobject _this, jint address, + jbyte value, jlong length) { + memset ((void *) ((jint) address), (jbyte) value, (jlong) length); +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: memmove + * Signature: (IIJ)V + */ +static void harmony_nio_memmove(JNIEnv *_env, jobject _this, jint destAddress, + jint srcAddress, jlong length) { + memmove ((void *) ((jint) destAddress), (const void *) ((jint) srcAddress), + (jlong) length); +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getByteImpl + * Signature: (I)B + */ +static jbyte harmony_nio_getByteImpl(JNIEnv *_env, jobject _this, + jint pointer) { + jbyte returnValue = *((jbyte *)pointer); + return returnValue; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getBytesImpl + * Signature: (I[BII)V + */ +static void harmony_nio_getBytesImpl(JNIEnv *_env, jobject _this, jint pointer, + jbyteArray dst, jint offset, jint length) { + jbyte *dst_ = (jbyte *)_env->GetPrimitiveArrayCritical(dst, (jboolean *)0); + memcpy(dst_ + offset, (jbyte *)pointer, length); + _env->ReleasePrimitiveArrayCritical(dst, dst_, 0); +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: putByteImpl + * Signature: (IB)V + */ +static void harmony_nio_putByteImpl(JNIEnv *_env, jobject _this, jint pointer, + jbyte val) { + *((jbyte *)pointer) = val; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: putBytesImpl + * Signature: (I[BII)V + */ +static void harmony_nio_putBytesImpl(JNIEnv *_env, jobject _this, + jint pointer, jbyteArray src, jint offset, jint length) { + jbyte *src_ = (jbyte *)_env->GetPrimitiveArrayCritical(src, (jboolean *)0); + memcpy((jbyte *)pointer, src_ + offset, length); + _env->ReleasePrimitiveArrayCritical(src, src_, JNI_ABORT); +} + +static void +swapShorts(jshort *shorts, int numBytes) { + jbyte *src = (jbyte *) shorts; + jbyte *dst = src; + int i; + + for (i = 0; i < numBytes; i+=2) { + jbyte b0 = *src++; + jbyte b1 = *src++; + *dst++ = b1; + *dst++ = b0; + } +} + +static void +swapInts(jint *ints, int numBytes) { + jbyte *src = (jbyte *) ints; + jbyte *dst = src; + int i; + for (i = 0; i < numBytes; i+=4) { + jbyte b0 = *src++; + jbyte b1 = *src++; + jbyte b2 = *src++; + jbyte b3 = *src++; + *dst++ = b3; + *dst++ = b2; + *dst++ = b1; + *dst++ = b0; + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: putShortsImpl + * Signature: (I[SIIZ)V + */ +static void harmony_nio_putShortsImpl(JNIEnv *_env, jobject _this, + jint pointer, jshortArray src, jint offset, jint length, jboolean swap) { + + offset = offset << 1; + length = length << 1; + + jshort *src_ = + (jshort *)_env->GetPrimitiveArrayCritical(src, (jboolean *)0); + if (swap) { + swapShorts(src_ + offset, length); + } + memcpy((jbyte *)pointer, src_ + offset, length); + if (swap) { + swapShorts(src_ + offset, length); + } + _env->ReleasePrimitiveArrayCritical(src, src_, JNI_ABORT); +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: putIntsImpl + * Signature: (I[IIIZ)V + */ +static void harmony_nio_putIntsImpl(JNIEnv *_env, jobject _this, + jint pointer, jintArray src, jint offset, jint length, jboolean swap) { + + offset = offset << 2; + length = length << 2; + + jint *src_ = + (jint *)_env->GetPrimitiveArrayCritical(src, (jboolean *)0); + if (swap) { + swapInts(src_ + offset, length); + } + memcpy((jbyte *)pointer, src_ + offset, length); + if (swap) { + swapInts(src_ + offset, length); + } + _env->ReleasePrimitiveArrayCritical(src, src_, JNI_ABORT); +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getShortImpl + * Signature: (I)S + */ +static jshort harmony_nio_getShortImpl(JNIEnv *_env, jobject _this, + jint pointer) { + if ((pointer & 0x1) == 0) { + jshort returnValue = *((jshort *)pointer); + return returnValue; + } else { + // Handle unaligned memory access one byte at a time + jshort s; + unsigned char *src = (unsigned char *) pointer; + unsigned char *dst = (unsigned char *) &s; + dst[0] = src[0]; + dst[1] = src[1]; + return s; + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: petShortImpl + * Signature: (IS)V + */ +static void harmony_nio_putShortImpl(JNIEnv *_env, jobject _this, jint pointer, + jshort value) { + if ((pointer & 0x1) == 0) { + *((jshort *)pointer) = value; + } else { + // Handle unaligned memory access one byte at a time + unsigned char *src = (unsigned char *) &value; + unsigned char *dst = (unsigned char *) pointer; + dst[0] = src[0]; + dst[1] = src[1]; + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getIntImpl + * Signature: (I)I + */ +static jint harmony_nio_getIntImpl(JNIEnv *_env, jobject _this, jint pointer) { + if ((pointer & 0x3) == 0) { + jint returnValue = *((jint *)pointer); + return returnValue; + } else { + // Handle unaligned memory access one byte at a time + jint i; + unsigned char *src = (unsigned char *) pointer; + unsigned char *dst = (unsigned char *) &i; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + return i; + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: putIntImpl + * Signature: (II)V + */ +static void harmony_nio_putIntImpl(JNIEnv *_env, jobject _this, jint pointer, + jint value) { + if ((pointer & 0x3) == 0) { + *((jint *)pointer) = value; + } else { + // Handle unaligned memory access one byte at a time + unsigned char *src = (unsigned char *) &value; + unsigned char *dst = (unsigned char *) pointer; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getLongImpl + * Signature: (I)Ljava/lang/Long; + */ +static jlong harmony_nio_getLongImpl(JNIEnv *_env, jobject _this, + jint pointer) { + if ((pointer & 0x7) == 0) { + jlong returnValue = *((jlong *)pointer); + return returnValue; + } else { + // Handle unaligned memory access one byte at a time + jlong l; + memcpy((void *) &l, (void *) pointer, sizeof(jlong)); + return l; + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: putLongImpl + * Signature: (IJ)V + */ +static void harmony_nio_putLongImpl(JNIEnv *_env, jobject _this, jint pointer, + jlong value) { + if ((pointer & 0x7) == 0) { + *((jlong *)pointer) = value; + } else { + // Handle unaligned memory access one byte at a time + memcpy((void *) pointer, (void *) &value, sizeof(jlong)); + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getFloatImpl + * Signature: (I)F + */ +static jfloat harmony_nio_getFloatImpl(JNIEnv *_env, jobject _this, + jint pointer) { + if ((pointer & 0x3) == 0) { + jfloat returnValue = *((jfloat *)pointer); + return returnValue; + } else { + // Handle unaligned memory access one byte at a time + jfloat f; + memcpy((void *) &f, (void *) pointer, sizeof(jfloat)); + return f; + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: setFloatImpl + * Signature: (IF)V + */ +static void harmony_nio_putFloatImpl(JNIEnv *_env, jobject _this, jint pointer, + jfloat value) { + if ((pointer & 0x3) == 0) { + *((jfloat *)pointer) = value; + } else { + // Handle unaligned memory access one byte at a time + memcpy((void *) pointer, (void *) &value, sizeof(jfloat)); + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getDoubleImpl + * Signature: (I)D + */ +static jdouble harmony_nio_getDoubleImpl(JNIEnv *_env, jobject _this, + jint pointer) { + if ((pointer & 0x7) == 0) { + jdouble returnValue = *((jdouble *)pointer); + return returnValue; + } else { + // Handle unaligned memory access one byte at a time + jdouble d; + memcpy((void *) &d, (void *) pointer, sizeof(jdouble)); + return d; + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: putDoubleImpl + * Signature: (ID)V + */ +static void harmony_nio_putDoubleImpl(JNIEnv *_env, jobject _this, jint pointer, + jdouble value) { + if ((pointer & 0x7) == 0) { + *((jdouble *)pointer) = value; + } else { + // Handle unaligned memory access one byte at a time + memcpy((void *) pointer, (void *) &value, sizeof(jdouble)); + } +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: getAddress + * Signature: (I)I + */ +static jint harmony_nio_getAddress(JNIEnv *_env, jobject _this, jint pointer) { + return (jint) * (int *) pointer; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: setAddress + * Signature: (II)V + */ +static void harmony_nio_setAddress(JNIEnv *_env, jobject _this, jint pointer, + jint value) { + *(int *) pointer = (int) value; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: mmapImpl + * Signature: (IJJI)I + */ +static jint harmony_nio_mmapImpl(JNIEnv *_env, jobject _this, jint fd, + jlong alignment, jlong size, jint mmode) { + void *mapAddress = NULL; + int prot, flags; + + // Convert from Java mapping mode to port library mapping mode. + switch (mmode) { + case MMAP_READ_ONLY: + prot = PROT_READ; + flags = MAP_SHARED; + break; + case MMAP_READ_WRITE: + prot = PROT_READ|PROT_WRITE; + flags = MAP_SHARED; + break; + case MMAP_WRITE_COPY: + prot = PROT_READ|PROT_WRITE; + flags = MAP_PRIVATE; + break; + default: + return -1; + } + + mapAddress = mmap(0, (size_t)(size&0x7fffffff), prot, flags,fd, + (off_t)(alignment&0x7fffffff)); + if (mapAddress == MAP_FAILED) { + return -1; + } + + return (jint) mapAddress; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: unmapImpl + * Signature: (IJ)V + */ +static void harmony_nio_unmapImpl(JNIEnv *_env, jobject _this, jint address, + jlong size) { + munmap((void *)address, (size_t)size); +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: loadImpl + * Signature: (IJ)I + */ +static jint harmony_nio_loadImpl(JNIEnv *_env, jobject _this, jint address, + jlong size) { + + if(mlock((void *)address, (size_t)size)!=-1) { + if(munlock((void *)address, (size_t)size)!=-1) { + return 0; /* normally */ + } + } + else { + /* according to linux sys call, only root can mlock memory. */ + if(errno == EPERM) { + return 0; + } + } + + return -1; +} + +int getPageSize() { + static int page_size = 0; + if(page_size==0) + { + page_size=getpagesize(); + } + return page_size; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: isLoadedImpl + * Signature: (IJ)Z + */ +static jboolean harmony_nio_isLoadedImpl(JNIEnv *_env, jobject _this, + jint address, jlong size) { + + jboolean result = 0; + jint m_addr = (jint)address; + int page_size = getPageSize(); + unsigned char* vec = NULL; + int page_count = 0; + + int align_offset = m_addr%page_size;// addr should align with the boundary of a page. + m_addr -= align_offset; + size += align_offset; + page_count = (size+page_size-1)/page_size; + + vec = (unsigned char *) malloc(page_count*sizeof(char)); + + if (mincore((void *)m_addr, size, (MINCORE_POINTER_TYPE) vec)==0) { + // or else there is error about the mincore and return false; + int i; + for(i=0 ;i<page_count;i++) { + if(vec[i]!=1) { + break; + } + } + if(i==page_count) { + result = 1; + } + } + + free(vec); + + return result; +} + +/* + * Class: org_apache_harmony_luni_platform_OSMemory + * Method: flushImpl + * Signature: (IJ)I + */ +static jint harmony_nio_flushImpl(JNIEnv *_env, jobject _this, jint address, + jlong size) { + return msync((void *)address, size, MS_SYNC); +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "isLittleEndianImpl", "()Z", (void*) harmony_nio_littleEndian }, + { "getPointerSizeImpl", "()I", (void*) harmony_nio_getPointerSizeImpl }, + { "malloc", "(I)I", (void*) harmony_nio_mallocImpl }, + { "free", "(I)V", (void*) harmony_nio_freeImpl }, + { "memset", "(IBJ)V", (void*) harmony_nio_memset }, + { "memmove", "(IIJ)V", (void*) harmony_nio_memmove }, + { "getByteArray", "(I[BII)V",(void*) harmony_nio_getBytesImpl }, + { "setByteArray", "(I[BII)V",(void*) harmony_nio_putBytesImpl }, + { "setShortArray", "(I[SIIZ)V",(void*) harmony_nio_putShortsImpl }, + { "setIntArray", "(I[IIIZ)V",(void*) harmony_nio_putIntsImpl }, + { "getByte", "(I)B", (void*) harmony_nio_getByteImpl }, + { "setByte", "(IB)V", (void*) harmony_nio_putByteImpl }, + { "getShort", "(I)S", (void*) harmony_nio_getShortImpl }, + { "setShort", "(IS)V", (void*) harmony_nio_putShortImpl }, + { "getInt", "(I)I", (void*) harmony_nio_getIntImpl }, + { "setInt", "(II)V", (void*) harmony_nio_putIntImpl }, + { "getLong", "(I)J", (void*) harmony_nio_getLongImpl }, + { "setLong", "(IJ)V", (void*) harmony_nio_putLongImpl }, + { "getFloat", "(I)F", (void*) harmony_nio_getFloatImpl }, + { "setFloat", "(IF)V", (void*) harmony_nio_putFloatImpl }, + { "getDouble", "(I)D", (void*) harmony_nio_getDoubleImpl }, + { "setDouble", "(ID)V", (void*) harmony_nio_putDoubleImpl }, + { "getAddress", "(I)I", (void*) harmony_nio_getAddress }, + { "setAddress", "(II)V", (void*) harmony_nio_setAddress }, + { "mmapImpl", "(IJJI)I", (void*) harmony_nio_mmapImpl }, + { "unmapImpl", "(IJ)V", (void*) harmony_nio_unmapImpl }, + { "loadImpl", "(IJ)I", (void*) harmony_nio_loadImpl }, + { "isLoadedImpl", "(IJ)Z", (void*) harmony_nio_isLoadedImpl }, + { "flushImpl", "(IJ)I", (void*) harmony_nio_flushImpl } +}; +int register_org_apache_harmony_luni_platform_OSMemory(JNIEnv *_env) { + return jniRegisterNativeMethods(_env, "org/apache/harmony/luni/platform/OSMemory", + gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp new file mode 100755 index 0000000..de01295 --- /dev/null +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp @@ -0,0 +1,3637 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "OSNetworkSystem" + +#include "JNIHelp.h" +#include "jni.h" +#include "errno.h" + +#include <unistd.h> +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/time.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/un.h> + +#include <cutils/properties.h> +#include <cutils/adb_networking.h> +#include <utils/LogSocket.h> +#include "AndroidSystemNatives.h" + +/** + * @name Socket Errors + * Error codes for socket operations + * + * @internal SOCKERR* range from -200 to -299 avoid overlap + */ +#define SOCKERR_BADSOCKET -200 /* generic error */ +#define SOCKERR_NOTINITIALIZED -201 /* socket library uninitialized */ +#define SOCKERR_BADAF -202 /* bad address family */ +#define SOCKERR_BADPROTO -203 /* bad protocol */ +#define SOCKERR_BADTYPE -204 /* bad type */ +#define SOCKERR_SYSTEMBUSY -205 /* system busy handling requests */ +#define SOCKERR_SYSTEMFULL -206 /* too many sockets */ +#define SOCKERR_NOTCONNECTED -207 /* socket is not connected */ +#define SOCKERR_INTERRUPTED -208 /* the call was cancelled */ +#define SOCKERR_TIMEOUT -209 /* the operation timed out */ +#define SOCKERR_CONNRESET -210 /* the connection was reset */ +#define SOCKERR_WOULDBLOCK -211 /* the socket is marked as nonblocking operation would block */ +#define SOCKERR_ADDRNOTAVAIL -212 /* address not available */ +#define SOCKERR_ADDRINUSE -213 /* address already in use */ +#define SOCKERR_NOTBOUND -214 /* the socket is not bound */ +#define SOCKERR_UNKNOWNSOCKET -215 /* resolution of fileDescriptor to socket failed */ +#define SOCKERR_INVALIDTIMEOUT -216 /* the specified timeout is invalid */ +#define SOCKERR_FDSETFULL -217 /* Unable to create an FDSET */ +#define SOCKERR_TIMEVALFULL -218 /* Unable to create a TIMEVAL */ +#define SOCKERR_REMSOCKSHUTDOWN -219 /* The remote socket has shutdown gracefully */ +#define SOCKERR_NOTLISTENING -220 /* listen() was not invoked prior to accept() */ +#define SOCKERR_NOTSTREAMSOCK -221 /* The socket does not support connection-oriented service */ +#define SOCKERR_ALREADYBOUND -222 /* The socket is already bound to an address */ +#define SOCKERR_NBWITHLINGER -223 /* The socket is marked non-blocking & SO_LINGER is non-zero */ +#define SOCKERR_ISCONNECTED -224 /* The socket is already connected */ +#define SOCKERR_NOBUFFERS -225 /* No buffer space is available */ +#define SOCKERR_HOSTNOTFOUND -226 /* Authoritative Answer Host not found */ +#define SOCKERR_NODATA -227 /* Valid name, no data record of requested type */ +#define SOCKERR_BOUNDORCONN -228 /* The socket has not been bound or is already connected */ +#define SOCKERR_OPNOTSUPP -229 /* The socket does not support the operation */ +#define SOCKERR_OPTUNSUPP -230 /* The socket option is not supported */ +#define SOCKERR_OPTARGSINVALID -231 /* The socket option arguments are invalid */ +#define SOCKERR_SOCKLEVELINVALID -232 /* The socket level is invalid */ +#define SOCKERR_TIMEOUTFAILURE -233 +#define SOCKERR_SOCKADDRALLOCFAIL -234 /* Unable to allocate the sockaddr structure */ +#define SOCKERR_FDSET_SIZEBAD -235 /* The calculated maximum size of the file descriptor set is bad */ +#define SOCKERR_UNKNOWNFLAG -236 /* The flag is unknown */ +#define SOCKERR_MSGSIZE -237 /* The datagram was too big to fit the specified buffer & was truncated. */ +#define SOCKERR_NORECOVERY -238 /* The operation failed with no recovery possible */ +#define SOCKERR_ARGSINVALID -239 /* The arguments are invalid */ +#define SOCKERR_BADDESC -240 /* The socket argument is not a valid file descriptor */ +#define SOCKERR_NOTSOCK -241 /* The socket argument is not a socket */ +#define SOCKERR_HOSTENTALLOCFAIL -242 /* Unable to allocate the hostent structure */ +#define SOCKERR_TIMEVALALLOCFAIL -243 /* Unable to allocate the timeval structure */ +#define SOCKERR_LINGERALLOCFAIL -244 /* Unable to allocate the linger structure */ +#define SOCKERR_IPMREQALLOCFAIL -245 /* Unable to allocate the ipmreq structure */ +#define SOCKERR_FDSETALLOCFAIL -246 /* Unable to allocate the fdset structure */ +#define SOCKERR_OPFAILED -247 /* Operation failed */ +#define SOCKERR_VALUE_NULL -248 /* The value indexed was NULL */ +#define SOCKERR_CONNECTION_REFUSED -249 /* connection was refused */ +#define SOCKERR_ENETUNREACH -250 /* network is not reachable */ +#define SOCKERR_EACCES -251 /* permissions do not allow action on socket */ +#define SOCKERR_EHOSTUNREACH -252 /* no route to host */ +#define SOCKERR_EPIPE -253 /* broken pipe */ + +#define JAVASOCKOPT_TCP_NODELAY 1 +#define JAVASOCKOPT_IP_TOS 3 +#define JAVASOCKOPT_SO_REUSEADDR 4 +#define JAVASOCKOPT_SO_KEEPALIVE 8 +#define JAVASOCKOPT_MCAST_TIME_TO_LIVE 10 /* Currently unused */ +#define JAVASOCKOPT_SO_BINDADDR 15 +#define JAVASOCKOPT_MCAST_INTERFACE 16 +#define JAVASOCKOPT_MCAST_TTL 17 +#define JAVASOCKOPT_IP_MULTICAST_LOOP 18 +#define JAVASOCKOPT_MCAST_ADD_MEMBERSHIP 19 +#define JAVASOCKOPT_MCAST_DROP_MEMBERSHIP 20 +#define JAVASOCKOPT_IP_MULTICAST_IF2 31 +#define JAVASOCKOPT_SO_BROADCAST 32 +#define JAVASOCKOPT_SO_LINGER 128 +#define JAVASOCKOPT_REUSEADDR_AND_REUSEPORT 10001 +#define JAVASOCKOPT_SO_SNDBUF 4097 +#define JAVASOCKOPT_SO_RCVBUF 4098 +#define JAVASOCKOPT_SO_RCVTIMEOUT 4102 +#define JAVASOCKOPT_SO_OOBINLINE 4099 + +/* constants for calling multi-call functions */ +#define SOCKET_STEP_START 10 +#define SOCKET_STEP_CHECK 20 +#define SOCKET_STEP_DONE 30 + +#define BROKEN_MULTICAST_IF 1 +#define BROKEN_MULTICAST_TTL 2 +#define BROKEN_TCP_NODELAY 4 + +#define SOCKET_CONNECT_STEP_START 0 +#define SOCKET_CONNECT_STEP_CHECK 1 + +#define SOCKET_OP_NONE 0 +#define SOCKET_OP_READ 1 +#define SOCKET_OP_WRITE 2 +#define SOCKET_READ_WRITE 3 + +#define SOCKET_MSG_PEEK 1 +#define SOCKET_MSG_OOB 2 + +#define SOCKET_NOFLAGS 0 + +#undef BUFFERSIZE +#define BUFFERSIZE 2048 + +// wait for 500000 usec = 0.5 second +#define SEND_RETRY_TIME 500000 + + +struct CachedFields { + jfieldID fd_descriptor; + jclass iaddr_class; + jmethodID iaddr_class_init; + jmethodID iaddr_getbyaddress; + jfieldID iaddr_ipaddress; + jclass genericipmreq_class; + jclass integer_class; + jmethodID integer_class_init; + jfieldID integer_class_value; + jclass boolean_class; + jmethodID boolean_class_init; + jfieldID boolean_class_value; + jclass byte_class; + jmethodID byte_class_init; + jfieldID byte_class_value; + jclass string_class; + jmethodID string_class_init; + jfieldID socketimpl_address; + jfieldID socketimpl_port; + jclass dpack_class; + jfieldID dpack_address; + jfieldID dpack_port; + jfieldID dpack_length; +} gCachedFields; + +static int useAdbNetworking = 0; + +/* needed for connecting with timeout */ +typedef struct selectFDSet { + int nfds; + int sock; + fd_set writeSet; + fd_set readSet; + fd_set exceptionSet; +} selectFDSet; + +static const char * netLookupErrorString(int anErrorNum); + +/** + * Throws an SocketException with the message affiliated with the errorCode. + */ +static void throwSocketException(JNIEnv *env, int errorCode) { + jniThrowException(env, "java/net/SocketException", + netLookupErrorString(errorCode)); +} + +/** + * Throws an IOException with the given message. + */ +static void throwIOExceptionStr(JNIEnv *env, const char *message) { + jniThrowException(env, "java/io/IOException", message); +} + +/** + * Throws a NullPointerException. + */ +static void throwNullPointerException(JNIEnv *env) { + jniThrowException(env, "java/lang/NullPointerException", NULL); +} + +/** + * Converts a 4-byte array to a native address structure. Throws a + * NullPointerException or an IOException in case of error. This is + * signaled by a return value of -1. The normal return value is 0. + */ +static int javaAddressToStructIn( + JNIEnv *env, jbyteArray java_address, struct in_addr *address) { + + memset(address, 0, sizeof(address)); + + if (java_address == NULL) { + return -1; + } + + if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { + return -1; + } + + jbyte * java_address_bytes + = env->GetByteArrayElements(java_address, NULL); + + memcpy(&(address->s_addr), + java_address_bytes, + sizeof(address->s_addr)); + + env->ReleaseByteArrayElements(java_address, java_address_bytes, JNI_ABORT); + + return 0; +} + +/** + * Converts a native address structure to a 4-byte array. Throws a + * NullPointerException or an IOException in case of error. This is + * signaled by a return value of -1. The normal return value is 0. + */ +static int structInToJavaAddress( + JNIEnv *env, struct in_addr *address, jbyteArray java_address) { + + if (java_address == NULL) { + return -1; + } + + if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { + return -1; + } + + jbyte *java_address_bytes; + + java_address_bytes = env->GetByteArrayElements(java_address, NULL); + + memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr)); + + env->ReleaseByteArrayElements(java_address, java_address_bytes, 0); + + return 0; +} + +/** + * Converts a native address structure to an InetAddress object. + * Throws a NullPointerException or an IOException in case of + * error. This is signaled by a return value of -1. The normal + * return value is 0. + */ +static int socketAddressToInetAddress(JNIEnv *env, + struct sockaddr_in *sockaddress, jobject inetaddress, int *port) { + + jbyteArray ipaddress; + int result; + + ipaddress = (jbyteArray)env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress); + + if (structInToJavaAddress(env, &sockaddress->sin_addr, ipaddress) < 0) { + return -1; + } + + *port = ntohs(sockaddress->sin_port); + + return 0; +} + +/** + * Converts an InetAddress object to a native address structure. + * Throws a NullPointerException or an IOException in case of + * error. This is signaled by a return value of -1. The normal + * return value is 0. + */ +static int inetAddressToSocketAddress(JNIEnv *env, + jobject inetaddress, int port, struct sockaddr_in *sockaddress) { + + jbyteArray ipaddress; + int result; + + ipaddress = (jbyteArray)env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress); + + memset(sockaddress, 0, sizeof(sockaddress)); + + sockaddress->sin_family = AF_INET; + sockaddress->sin_port = htons(port); + + if (javaAddressToStructIn(env, ipaddress, &(sockaddress->sin_addr)) < 0) { + return -1; + } + + return 0; +} + +static jobject structInToInetAddress(JNIEnv *env, struct in_addr *address) { + jbyteArray bytes; + int success; + + bytes = env->NewByteArray(4); + + if (bytes == NULL) { + return NULL; + } + + if (structInToJavaAddress(env, address, bytes) < 0) { + return NULL; + } + + return env->CallStaticObjectMethod(gCachedFields.iaddr_class, + gCachedFields.iaddr_getbyaddress, bytes); +} + +/** + * Answer a new java.lang.Boolean object. + * + * @param env pointer to the JNI library + * @param anInt the Boolean constructor argument + * + * @return the new Boolean + */ + +jobject newJavaLangBoolean(JNIEnv * env, jint anInt) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.boolean_class; + tempMethod = gCachedFields.boolean_class_init; + return env->NewObject(tempClass, tempMethod, (jboolean) (anInt != 0)); +} + +/** + * Answer a new java.lang.Byte object. + * + * @param env pointer to the JNI library + * @param anInt the Byte constructor argument + * + * @return the new Byte + */ + +jobject newJavaLangByte(JNIEnv * env, jbyte val) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.byte_class; + tempMethod = gCachedFields.byte_class_init; + return env->NewObject(tempClass, tempMethod, val); +} + +/** + * Answer a new java.lang.Integer object. + * + * @param env pointer to the JNI library + * @param anInt the Integer constructor argument + * + * @return the new Integer + */ + +jobject newJavaLangInteger(JNIEnv * env, jint anInt) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.integer_class; + tempMethod = gCachedFields.integer_class_init; + return env->NewObject(tempClass, tempMethod, anInt); +} + +/** + * Answer a new java.lang.String object. + * + * @param env pointer to the JNI library + * @param anInt the byte[] constructor argument + * + * @return the new String + */ + +jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.string_class; + tempMethod = gCachedFields.string_class_init; + return env->NewObject(tempClass, tempMethod, (jbyteArray) bytes); +} + +/** + * Query OS for timestamp. + * Retrieve the current value of system clock and convert to milliseconds. + * + * @param[in] portLibrary The port library. + * + * @return 0 on failure, time value in milliseconds on success. + * @deprecated Use @ref time_hires_clock and @ref time_hires_delta + * + * technically, this should return I_64 since both timeval.tv_sec and + * timeval.tv_usec are long + */ + +static int time_msec_clock() { + struct timeval tp; + struct timezone tzp; + + gettimeofday(&tp, &tzp); + return (tp.tv_sec * 1000) + (tp.tv_usec / 1000); +} + +/** + * check if the passed sockaddr_in struct contains a localhost address + * + * @param[in] address pointer to the address to check + * + * @return 0 if the passed address isn't a localhost address + */ +static int isLocalhost(struct sockaddr_in *address) { + // return address == 127.0.0.1 + return (unsigned int) address->sin_addr.s_addr == 16777343; +} + +/** + * Answer the errorString corresponding to the errorNumber, if available. + * This function will answer a default error string, if the errorNumber is not + * recognized. + * + * This function will have to be reworked to handle internationalization + * properly, removing the explicit strings. + * + * @param anErrorNum the error code to resolve to a human readable string + * + * @return a human readable error string + */ + +static const char * netLookupErrorString(int anErrorNum) { + switch (anErrorNum) { + case SOCKERR_BADSOCKET: + return "Bad socket"; + case SOCKERR_NOTINITIALIZED: + return "Socket library uninitialized"; + case SOCKERR_BADAF: + return "Bad address family"; + case SOCKERR_BADPROTO: + return "Bad protocol"; + case SOCKERR_BADTYPE: + return "Bad type"; + case SOCKERR_SYSTEMBUSY: + return "System busy handling requests"; + case SOCKERR_SYSTEMFULL: + return "Too many sockets allocated"; + case SOCKERR_NOTCONNECTED: + return "Socket is not connected"; + case SOCKERR_INTERRUPTED: + return "The system call was cancelled"; + case SOCKERR_TIMEOUT: + return "The operation timed out"; + case SOCKERR_CONNRESET: + return "The connection was reset"; + case SOCKERR_WOULDBLOCK: + return "The nonblocking operation would block"; + case SOCKERR_ADDRNOTAVAIL: + return "The address is not available"; + case SOCKERR_ADDRINUSE: + return "The address is already in use"; + case SOCKERR_NOTBOUND: + return "The socket is not bound"; + case SOCKERR_UNKNOWNSOCKET: + return "Resolution of the FileDescriptor to socket failed"; + case SOCKERR_INVALIDTIMEOUT: + return "The specified timeout is invalid"; + case SOCKERR_FDSETFULL: + return "Unable to create an FDSET"; + case SOCKERR_TIMEVALFULL: + return "Unable to create a TIMEVAL"; + case SOCKERR_REMSOCKSHUTDOWN: + return "The remote socket has shutdown gracefully"; + case SOCKERR_NOTLISTENING: + return "Listen() was not invoked prior to accept()"; + case SOCKERR_NOTSTREAMSOCK: + return "The socket does not support connection-oriented service"; + case SOCKERR_ALREADYBOUND: + return "The socket is already bound to an address"; + case SOCKERR_NBWITHLINGER: + return "The socket is marked non-blocking & SO_LINGER is non-zero"; + case SOCKERR_ISCONNECTED: + return "The socket is already connected"; + case SOCKERR_NOBUFFERS: + return "No buffer space is available"; + case SOCKERR_HOSTNOTFOUND: + return "Authoritative Answer Host not found"; + case SOCKERR_NODATA: + return "Valid name, no data record of requested type"; + case SOCKERR_BOUNDORCONN: + return "The socket has not been bound or is already connected"; + case SOCKERR_OPNOTSUPP: + return "The socket does not support the operation"; + case SOCKERR_OPTUNSUPP: + return "The socket option is not supported"; + case SOCKERR_OPTARGSINVALID: + return "The socket option arguments are invalid"; + case SOCKERR_SOCKLEVELINVALID: + return "The socket level is invalid"; + case SOCKERR_TIMEOUTFAILURE: + return "The timeout operation failed"; + case SOCKERR_SOCKADDRALLOCFAIL: + return "Failed to allocate address structure"; + case SOCKERR_FDSET_SIZEBAD: + return "The calculated maximum size of the file descriptor set is bad"; + case SOCKERR_UNKNOWNFLAG: + return "The flag is unknown"; + case SOCKERR_MSGSIZE: + return "The datagram was too big to fit the specified buffer, so truncated"; + case SOCKERR_NORECOVERY: + return "The operation failed with no recovery possible"; + case SOCKERR_ARGSINVALID: + return "The arguments are invalid"; + case SOCKERR_BADDESC: + return "The socket argument is not a valid file descriptor"; + case SOCKERR_NOTSOCK: + return "The socket argument is not a socket"; + case SOCKERR_HOSTENTALLOCFAIL: + return "Unable to allocate the hostent structure"; + case SOCKERR_TIMEVALALLOCFAIL: + return "Unable to allocate the timeval structure"; + case SOCKERR_LINGERALLOCFAIL: + return "Unable to allocate the linger structure"; + case SOCKERR_IPMREQALLOCFAIL: + return "Unable to allocate the ipmreq structure"; + case SOCKERR_FDSETALLOCFAIL: + return "Unable to allocate the fdset structure"; + case SOCKERR_OPFAILED: + return "Operation failed"; + case SOCKERR_CONNECTION_REFUSED: + return "Connection refused"; + case SOCKERR_ENETUNREACH: + return "Network unreachable"; + case SOCKERR_EHOSTUNREACH: + return "No route to host"; + case SOCKERR_EPIPE: + return "Broken pipe"; + case SOCKERR_EACCES: + return "Permission denied (maybe missing INTERNET permission)"; + + default: + LOGE("unknown socket error %d", anErrorNum); + return "unknown error"; + } +} + +static int convertError(int errorCode) { + switch (errorCode) { + case EBADF: + return SOCKERR_BADDESC; + case ENOBUFS: + return SOCKERR_NOBUFFERS; + case EOPNOTSUPP: + return SOCKERR_OPNOTSUPP; + case ENOPROTOOPT: + return SOCKERR_OPTUNSUPP; + case EINVAL: + return SOCKERR_SOCKLEVELINVALID; + case ENOTSOCK: + return SOCKERR_NOTSOCK; + case EINTR: + return SOCKERR_INTERRUPTED; + case ENOTCONN: + return SOCKERR_NOTCONNECTED; + case EAFNOSUPPORT: + return SOCKERR_BADAF; + /* note: CONNRESET not included because it has the same + * value as ECONNRESET and they both map to SOCKERR_CONNRESET */ + case ECONNRESET: + return SOCKERR_CONNRESET; + case EAGAIN: + return SOCKERR_WOULDBLOCK; + case EPROTONOSUPPORT: + return SOCKERR_BADPROTO; + case EFAULT: + return SOCKERR_ARGSINVALID; + case ETIMEDOUT: + return SOCKERR_TIMEOUT; + case ECONNREFUSED: + return SOCKERR_CONNECTION_REFUSED; + case ENETUNREACH: + return SOCKERR_ENETUNREACH; + case EACCES: + return SOCKERR_EACCES; + case EPIPE: + return SOCKERR_EPIPE; + case EHOSTUNREACH: + return SOCKERR_EHOSTUNREACH; + case EADDRINUSE: + return SOCKERR_ADDRINUSE; + case EADDRNOTAVAIL: + return SOCKERR_ADDRNOTAVAIL; + case EMSGSIZE: + return SOCKERR_MSGSIZE; + default: + LOGE("unclassified errno %d (%s)", errorCode, strerror(errorCode)); + return SOCKERR_OPFAILED; + } +} + +static int sockSelect(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) { + + int result = select(nfds, readfds, writefds, exceptfds, timeout); + + if (result < 0) { + if (errno == EINTR) { + result = SOCKERR_INTERRUPTED; + } else { + result = SOCKERR_OPFAILED; + } + } else if (result == 0) { + result = SOCKERR_TIMEOUT; + } + return result; +} + +#define SELECT_READ_TYPE 0 +#define SELECT_WRITE_TYPE 1 + +static int selectWait(int handle, int uSecTime, int type) { + fd_set fdset; + struct timeval time, *timePtr; + int result = 0; + int size = handle + 1; + + FD_ZERO(&fdset); + FD_SET(handle, &fdset); + + if (0 <= uSecTime) { + /* Use a timeout if uSecTime >= 0 */ + memset(&time, 0, sizeof(time)); + time.tv_usec = uSecTime; + timePtr = &time; + } else { + /* Infinite timeout if uSecTime < 0 */ + timePtr = NULL; + } + + if (type == SELECT_READ_TYPE) { + result = sockSelect(size, &fdset, NULL, NULL, timePtr); + } else { + result = sockSelect(size, NULL, &fdset, NULL, timePtr); + } + return result; +} + +static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int type) { + /* now try reading the socket for the timespan timeout. + * if timeout is 0 try forever until the soclets gets ready or until an + * exception occurs. + */ + int pollTimeoutUSec = 100000, pollMsec = 100; + int finishTime = 0; + int timeLeft = timeout; + int hasTimeout = timeout > 0 ? 1 : 0; + int result = 0; + int handle; + + if (hasTimeout) { + finishTime = time_msec_clock() + timeout; + } + + int poll = 1; + + while (poll) { /* begin polling loop */ + + /* + * Fetch the handle every time in case the socket is closed. + */ + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_INTERRUPTED); + return -1; + } + + if (hasTimeout) { + + if (timeLeft - 10 < pollMsec) { + pollTimeoutUSec = timeLeft <= 0 ? 0 : (timeLeft * 1000); + } + + result = selectWait(handle, pollTimeoutUSec, type); + + /* + * because we are polling at a time smaller than timeout + * (presumably) lets treat an interrupt and timeout the same - go + * see if we're done timewise, and then just try again if not. + */ + if (SOCKERR_TIMEOUT == result || + SOCKERR_INTERRUPTED == result) { + + timeLeft = finishTime - time_msec_clock(); + + if (timeLeft <= 0) { + /* + * Always throw the "timeout" message because that is + * effectively what has happened, even if we happen to + * have been interrupted. + */ + jniThrowException(env, "java/net/SocketTimeoutException", + netLookupErrorString(SOCKERR_TIMEOUT)); + } else { + continue; // try again + } + + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + } + poll = 0; + + } else { /* polling with no timeout (why would you do this?)*/ + + result = selectWait(handle, pollTimeoutUSec, type); + + /* + * if interrupted (or a timeout) just retry + */ + if (SOCKERR_TIMEOUT == result || + SOCKERR_INTERRUPTED == result) { + + continue; // try again + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + } + poll = 0; + } + } /* end polling loop */ + + return result; +} + +/** + * A helper method, to set the connect context to a Long object. + * + * @param env pointer to the JNI library + * @param longclass Java Long Object + */ +void setConnectContext(JNIEnv *env,jobject longclass,jbyte * context) { + jclass descriptorCLS; + jfieldID descriptorFID; + descriptorCLS = env->FindClass("java/lang/Long"); + descriptorFID = env->GetFieldID(descriptorCLS, "value", "J"); + env->SetLongField(longclass, descriptorFID, (jlong)((jint)context)); +}; + +/** + * A helper method, to get the connect context. + * + * @param env pointer to the JNI library + * @param longclass Java Long Object + */ +jbyte *getConnectContext(JNIEnv *env, jobject longclass) { + jclass descriptorCLS; + jfieldID descriptorFID; + descriptorCLS = env->FindClass("java/lang/Long"); + descriptorFID = env->GetFieldID(descriptorCLS, "value", "J"); + return (jbyte*) ((jint)env->GetLongField(longclass, descriptorFID)); +}; + +// typical ip checksum +unsigned short ip_checksum(unsigned short* buffer, int size) { + register unsigned short * buf = buffer; + register int bufleft = size; + register unsigned long sum = 0; + + while (bufleft > 1) { + sum = sum + (*buf++); + bufleft = bufleft - sizeof(unsigned short ); + } + if (bufleft) { + sum = sum + (*(unsigned char*)buf); + } + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + return (unsigned short )(~sum); +} + +/** + * Establish a connection to a peer with a timeout. This function is called + * repeatedly in order to carry out the connect and to allow other tasks to + * proceed on certain platforms. The caller must first call with + * step = SOCKET_STEP_START, if the result is SOCKERR_NOTCONNECTED it will then + * call it with step = CHECK until either another error or 0 is returned to + * indicate the connect is complete. Each time the function should sleep for no + * more than timeout milliseconds. If the connect succeeds or an error occurs, + * the caller must always end the process by calling the function with + * step = SOCKET_STEP_DONE + * + * @param[in] portLibrary The port library. + * @param[in] sock pointer to the unconnected local socket. + * @param[in] addr pointer to the sockaddr, specifying remote host/port. + * @param[in] timeout the timeout in milliseconds. If timeout is negative, + * perform a block operation. + * @param[in,out] pointer to context pointer. Filled in on first call and then + * to be passed into each subsequent call. + * + * @return 0, if no errors occurred, otherwise the (negative) error code. + */ +static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, + unsigned int timeout, unsigned int step, jbyte *ctxt) { + int rc = 0; + struct timeval passedTimeout; + int errorVal; + socklen_t errorValLen = sizeof(int); + struct selectFDSet *context = NULL; + + if (SOCKET_STEP_START == step) { + + context = (struct selectFDSet *) ctxt; + + context->sock = handle; + context->nfds = handle + 1; + + if (useAdbNetworking && !isLocalhost(&addr)) { + + // LOGD("+connect to address 0x%08x (via adb)", + // addr.sin_addr.s_addr); + rc = adb_networking_connect_fd(handle, &addr); + // LOGD("-connect ret %d errno %d (via adb)", rc, errno); + + } else { + log_socket_connect(handle, ntohl(addr.sin_addr.s_addr), + ntohs(addr.sin_port)); + /* set the socket to non-blocking */ + int block = JNI_TRUE; + rc = ioctl(handle, FIONBIO, &block); + if (0 != rc) { + return convertError(rc); + } + + // LOGD("+connect to address 0x%08x (via normal) on handle %d", + // addr.sin_addr.s_addr, handle); + do { + rc = connect(handle, (struct sockaddr *) &addr, + sizeof(struct sockaddr)); + } while (rc < 0 && errno == EINTR); + // LOGD("-connect to address 0x%08x (via normal) returned %d", + // addr.sin_addr.s_addr, (int) rc); + + } + + if (rc == -1) { + rc = errno; + switch (rc) { + case EINTR: + return SOCKERR_ALREADYBOUND; + case EAGAIN: + case EINPROGRESS: + return SOCKERR_NOTCONNECTED; + default: + return convertError(rc); + } + } + + /* we connected right off the bat so just return */ + return rc; + + } else if (SOCKET_STEP_CHECK == step) { + /* now check if we have connected yet */ + + context = (struct selectFDSet *) ctxt; + + /* + * set the timeout value to be used. Because on some unix platforms we + * don't get notified when a socket is closed we only sleep for 100ms + * at a time + */ + passedTimeout.tv_sec = 0; + if (timeout > 100) { + passedTimeout.tv_usec = 100 * 1000; + } else if ((int)timeout >= 0) { + passedTimeout.tv_usec = timeout * 1000; + } + + /* initialize the FD sets for the select */ + FD_ZERO(&(context->exceptionSet)); + FD_ZERO(&(context->writeSet)); + FD_ZERO(&(context->readSet)); + FD_SET(context->sock, &(context->writeSet)); + FD_SET(context->sock, &(context->readSet)); + FD_SET(context->sock, &(context->exceptionSet)); + + rc = select(context->nfds, + &(context->readSet), + &(context->writeSet), + &(context->exceptionSet), + (int)timeout >= 0 ? &passedTimeout : NULL); + + /* if there is at least one descriptor ready to be checked */ + if (0 < rc) { + /* if the descriptor is in the write set we connected or failed */ + if (FD_ISSET(context->sock, &(context->writeSet))) { + + if (!FD_ISSET(context->sock, &(context->readSet))) { + /* ok we have connected ok */ + return 0; + } else { + /* ok we have more work to do to figure it out */ + if (getsockopt(context->sock, SOL_SOCKET, SO_ERROR, + &errorVal, &errorValLen) >= 0) { + return errorVal ? convertError(errorVal) : 0; + } else { + return convertError(errno); + } + } + } + + /* if the descriptor is in the exception set the connect failed */ + if (FD_ISSET(context->sock, &(context->exceptionSet))) { + if (getsockopt(context->sock, SOL_SOCKET, SO_ERROR, &errorVal, + &errorValLen) >= 0) { + return errorVal ? convertError(errorVal) : 0; + } + rc = errno; + return convertError(rc); + } + + } else if (rc < 0) { + /* something went wrong with the select call */ + rc = errno; + + /* if it was EINTR we can just try again. Return not connected */ + if (EINTR == rc) { + return SOCKERR_NOTCONNECTED; + } + + /* some other error occured so look it up and return */ + return convertError(rc); + } + + /* + * if we get here the timeout expired or the connect had not yet + * completed just indicate that the connect is not yet complete + */ + return SOCKERR_NOTCONNECTED; + } else if (SOCKET_STEP_DONE == step) { + /* we are done the connect or an error occured so clean up */ + if (handle != -1) { + int block = JNI_FALSE; + ioctl(handle, FIONBIO, &block); + } + return 0; + } + return SOCKERR_ARGSINVALID; +} + +/** + * Join/Leave the nominated multicast group on the specified socket. + * Implemented by setting the multicast 'add membership'/'drop membership' + * option at the HY_IPPROTO_IP level on the socket. + * + * Implementation note for multicast sockets in general: + * + * - This code is untested, because at the time of this writing multicast can't + * be properly tested on Android due to GSM routing restrictions. So it might + * or might not work. + * + * - The REUSEPORT socket option that Harmony employs is not supported on Linux + * and thus also not supported on Android. It's is not needed for multicast + * to work anyway (REUSEADDR should suffice). + * + * @param env pointer to the JNI library. + * @param socketP pointer to the hysocket to join/leave on. + * @param optVal pointer to the InetAddress, the multicast group to join/drop. + * + * @exception SocketException if an error occurs during the call + */ +static void mcastAddDropMembership (JNIEnv * env, int handle, jobject optVal, + int ignoreIF, int setSockOptVal) { + int result; + struct ip_mreq ipmreqP; + struct sockaddr_in sockaddrP; + int length = sizeof(struct ip_mreq); + socklen_t lengthIF = sizeof(struct sockaddr_in); + + /* + * JNI objects needed to access the information in the optVal oject + * passed in. The object passed in is a GenericIPMreq object + */ + jclass cls; + jfieldID multiaddrID; + jfieldID interfaceAddrID; + jobject multiaddr; + jobject interfaceAddr; + + /* + * check whether we are getting an InetAddress or an Generic IPMreq, for now + * we support both so that we will not break the tests + */ + if (env->IsInstanceOf (optVal, gCachedFields.iaddr_class)) { + + ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); + if (!ignoreIF) { + + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockaddrP, + &lengthIF); + + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + + memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); + } + + result = inetAddressToSocketAddress(env, optVal, 0, &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); + + result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + + } else { + + /* we need the multicast address regardless of the type of address */ + cls = env->GetObjectClass(optVal); + multiaddrID = env->GetFieldID(cls, "multiaddr", "Ljava/net/InetAddress;"); + multiaddr = env->GetObjectField(optVal, multiaddrID); + + result = inetAddressToSocketAddress(env, multiaddr, 0, &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); + + /* we need to use an IP_MREQ as it is an IPV4 address */ + interfaceAddrID = env->GetFieldID(cls, "interfaceAddr", + "Ljava/net/InetAddress;"); + interfaceAddr = env->GetObjectField(optVal, interfaceAddrID); + + ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); + + /* + * if an interfaceAddr was passed then use that value, otherwise set the + * interface to all 0 to indicate the system should select the interface + * used + */ + if (!ignoreIF) { + if (NULL != interfaceAddr) { + + result = inetAddressToSocketAddress(env, interfaceAddr, 0, + &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); + + } + } + + /* join/drop the multicast address */ + result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + } +} + +static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, + jboolean jcl_supports_ipv6) { + // LOGD("ENTER oneTimeInitializationImpl of OSNetworkSystem"); + + char useAdbNetworkingProperty[PROPERTY_VALUE_MAX]; + char adbConnectedProperty[PROPERTY_VALUE_MAX]; + + property_get("android.net.use-adb-networking", useAdbNetworkingProperty, ""); + property_get("adb.connected", adbConnectedProperty, ""); + + if (strlen((char *)useAdbNetworkingProperty) > 0 + && strlen((char *)adbConnectedProperty) > 0) { + useAdbNetworking = 1; + } + + memset(&gCachedFields, 0, sizeof(gCachedFields)); + + // initializing InetAddress + + jclass iaddrclass = env->FindClass("java/net/InetAddress"); + + if (iaddrclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.InetAddress"); + return; + } + + gCachedFields.iaddr_class = (jclass) env->NewGlobalRef(iaddrclass); + + jmethodID iaddrclassinit = env->GetMethodID(iaddrclass, "<init>", "()V"); + + if (iaddrclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "InetAddress.<init>()"); + return; + } + + gCachedFields.iaddr_class_init = iaddrclassinit; + + jmethodID iaddrgetbyaddress = env->GetStaticMethodID(iaddrclass, + "getByAddress", "([B)Ljava/net/InetAddress;"); + + if (iaddrgetbyaddress == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "InetAddress.getByAddress(byte[] val)"); + return; + } + + gCachedFields.iaddr_getbyaddress = iaddrgetbyaddress; + + jfieldID iaddripaddress = env->GetFieldID(iaddrclass, "ipaddress", "[B"); + + if (iaddripaddress == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "Can't find field InetAddress.ipaddress"); + return; + } + + gCachedFields.iaddr_ipaddress = iaddripaddress; + + // get the GenericIPMreq class + + jclass genericipmreqclass = env->FindClass("org/apache/harmony/luni/net/GenericIPMreq"); + + if (genericipmreqclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "org.apache.harmony.luni.net.GenericIPMreq"); + return; + } + + gCachedFields.genericipmreq_class = (jclass) env->NewGlobalRef(genericipmreqclass); + + // initializing Integer + + jclass integerclass = env->FindClass("java/lang/Integer"); + + if (integerclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Integer"); + return; + } + + jmethodID integerclassinit = env->GetMethodID(integerclass, "<init>", "(I)V"); + + if (integerclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Integer.<init>(int val)"); + return; + } + + jfieldID integerclassvalue = env->GetFieldID(integerclass, "value", "I"); + + if (integerclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Integer.value"); + return; + } + + gCachedFields.integer_class = (jclass) env->NewGlobalRef(integerclass); + gCachedFields.integer_class_init = integerclassinit; + gCachedFields.integer_class_value = integerclassvalue; + + // initializing Boolean + + jclass booleanclass = env->FindClass("java/lang/Boolean"); + + if (booleanclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Boolean"); + return; + } + + jmethodID booleanclassinit = env->GetMethodID(booleanclass, "<init>", "(Z)V"); + + if (booleanclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Boolean.<init>(boolean val)"); + return; + } + + jfieldID booleanclassvalue = env->GetFieldID(booleanclass, "value", "Z"); + + if (booleanclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Boolean.value"); + return; + } + + gCachedFields.boolean_class = (jclass) env->NewGlobalRef(booleanclass); + gCachedFields.boolean_class_init = booleanclassinit; + gCachedFields.boolean_class_value = booleanclassvalue; + + // initializing Byte + + jclass byteclass = env->FindClass("java/lang/Byte"); + + if (byteclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Byte"); + return; + } + + jmethodID byteclassinit = env->GetMethodID(byteclass, "<init>", "(B)V"); + + if (byteclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Byte.<init>(byte val)"); + return; + } + + jfieldID byteclassvalue = env->GetFieldID(byteclass, "value", "B"); + + if (byteclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Byte.value"); + return; + } + + gCachedFields.byte_class = (jclass) env->NewGlobalRef(byteclass); + gCachedFields.byte_class_init = byteclassinit; + gCachedFields.byte_class_value = byteclassvalue; + + // initializing String + + jclass stringclass = env->FindClass("java/lang/String"); + + if (stringclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.String"); + return; + } + + jmethodID stringclassinit = env->GetMethodID(stringclass, "<init>", "([B)V"); + + if (stringclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "String.<init>(byte[] val)"); + return; + } + + gCachedFields.string_class = (jclass) env->NewGlobalRef(stringclass); + gCachedFields.string_class_init = stringclassinit; + + // initializing ScoketImpl + + jclass socketimplclass = env->FindClass("java/net/SocketImpl"); + + if (socketimplclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.SocketImpl"); + return; + } + + jfieldID socketimplport = env->GetFieldID(socketimplclass, "port", "I"); + + if (socketimplport == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", "SocketImpl.port"); + return; + } + + jfieldID socketimpladdress = env->GetFieldID(socketimplclass, "address", + "Ljava/net/InetAddress;"); + + if (socketimpladdress == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "SocketImpl.address"); + return; + } + + gCachedFields.socketimpl_address = socketimpladdress; + gCachedFields.socketimpl_port = socketimplport; + + gCachedFields.dpack_class = env->FindClass("java/net/DatagramPacket"); + if (gCachedFields.dpack_class == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.DatagramPacket"); + return; + } + + gCachedFields.dpack_address = env->GetFieldID(gCachedFields.dpack_class, + "address", "Ljava/net/InetAddress;"); + if (gCachedFields.dpack_address == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.address"); + return; + } + + gCachedFields.dpack_port = env->GetFieldID(gCachedFields.dpack_class, + "port", "I"); + if (gCachedFields.dpack_port == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.port"); + return; + } + + gCachedFields.dpack_length = env->GetFieldID(gCachedFields.dpack_class, + "length", "I"); + if (gCachedFields.dpack_length == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.length"); + return; + } + +} + +static void osNetworkSystem_createSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createSocketImpl"); + + int ret = socket(PF_INET, SOCK_STREAM, 0); + + if (ret < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, ret); + + return; +} + +static void osNetworkSystem_createDatagramSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createDatagramSocketImpl"); + + int ret = socket(PF_INET, SOCK_DGRAM, 0); + + if (ret < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, ret); + + return; +} + +static jint osNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint address, jint offset, jint count, + jint timeout) { + // LOGD("ENTER readSocketDirectImpl"); + + int handle; + jbyte *message = (jbyte *)address; + int result, ret, localCount; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = selectWait(handle, timeout, SELECT_READ_TYPE); + + if (0 > result) { + return 0; + } + + localCount = (count < 65536) ? count : 65536; + + do { + ret = recv(handle, (jbyte *) message, localCount, SOCKET_NOFLAGS); + } while (ret < 0 && errno == EINTR); + + if (0 == ret) { + return -1; + } else if (ret == -1) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + add_recv_stats(handle, ret); + return ret; +} + +static jint osNetworkSystem_readSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count, + jint timeout) { + // LOGD("ENTER readSocketImpl"); + + jbyte *message; + int result, localCount; + + jbyte internalBuffer[BUFFERSIZE]; + + localCount = (count < 65536) ? count : 65536; + + if (localCount > BUFFERSIZE) { + message = (jbyte*)malloc(localCount * sizeof(jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for readSocket"); + return 0; + } + } else { + message = (jbyte *)internalBuffer; + } + + result = osNetworkSystem_readSocketDirectImpl(env, clazz, fileDescriptor, + (jint) message, offset, count, timeout); + + if (result > 0) { + env->SetByteArrayRegion(data, offset, result, (jbyte *)message); + } + + if (((jbyte *)message) != internalBuffer) { + free(( jbyte *)message); + } + + return result; +} + +static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint address, jint offset, jint count) { + // LOGD("ENTER writeSocketDirectImpl"); + + int handle; + jbyte *message = (jbyte *)address; + int result = 0, sent = 0; + + if (count <= 0) { + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = send(handle, (jbyte *) message, (int) count, SOCKET_NOFLAGS); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + + if (SOCKERR_WOULDBLOCK == err){ + jclass socketExClass,errorCodeExClass; + jmethodID errorCodeExConstructor, socketExConstructor,socketExCauseMethod; + jobject errorCodeEx, socketEx; + const char* errorMessage = netLookupErrorString(err); + jstring errorMessageString = env->NewStringUTF(errorMessage); + + errorCodeExClass = env->FindClass("org/apache/harmony/luni/util/ErrorCodeException"); + if (!errorCodeExClass){ + return 0; + } + errorCodeExConstructor = env->GetMethodID(errorCodeExClass,"<init>","(I)V"); + if (!errorCodeExConstructor){ + return 0; + } + errorCodeEx = env->NewObject(errorCodeExClass,errorCodeExConstructor,err); + + socketExClass = env->FindClass("java/net/SocketException"); + if (!socketExClass) { + return 0; + } + socketExConstructor = env->GetMethodID(socketExClass,"<init>","(Ljava/lang/String;)V"); + if (!socketExConstructor) { + return 0; + } + socketEx = env->NewObject(socketExClass, socketExConstructor, errorMessageString); + socketExCauseMethod = env->GetMethodID(socketExClass,"initCause","(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); + env->CallObjectMethod(socketEx,socketExCauseMethod,errorCodeEx); + env->Throw((jthrowable)socketEx); + return 0; + } + throwSocketException(env, err); + return 0; + } + + add_send_stats(handle, result); + return result; +} + +static jint osNetworkSystem_writeSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count) { + // LOGD("ENTER writeSocketImpl"); + + jbyte *message; + int sent = 0; + jint result = 0; + +/* TODO: ARRAY PINNING */ +#define INTERNAL_SEND_BUFFER_MAX 512 + jbyte internalBuffer[INTERNAL_SEND_BUFFER_MAX]; + + if (count > INTERNAL_SEND_BUFFER_MAX) { + message = (jbyte*)malloc(count * sizeof( jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for writeSocket"); + return 0; + } + } else { + message = (jbyte *)internalBuffer; + } + + env->GetByteArrayRegion(data, offset, count, message); + + result = osNetworkSystem_writeSocketDirectImpl(env, clazz, fileDescriptor, + (jint) message, offset, count); + + if (( jbyte *)message != internalBuffer) { + free(( jbyte *)message); + } +#undef INTERNAL_SEND_BUFFER_MAX + return result; +} + +static void osNetworkSystem_setNonBlockingImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean nonblocking) { + // LOGD("ENTER setNonBlockingImpl"); + + int handle; + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + int block = nonblocking; + + result = ioctl(handle, FIONBIO, &block); + + if (result == -1) { + throwSocketException(env, convertError(errno)); + } +} + +static jint osNetworkSystem_connectSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint trafficClass, jobject inetAddr, jint port); + +static jint osNetworkSystem_connectWithTimeoutSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jint timeout, jint trafficClass, + jobject inetAddr, jint port, jint step, jbyteArray passContext) { + // LOGD("ENTER connectWithTimeoutSocketImpl"); + + int handle; + int result = 0; + struct sockaddr_in address; + jbyte *context = NULL; + + memset(&address, 0, sizeof(address)); + + address.sin_family = AF_INET; + + result = inetAddressToSocketAddress(env, inetAddr, port, + (struct sockaddr_in *) &address); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return result; + } + + // Check if we're using adb networking and redirect in case it is used. + if (useAdbNetworking && !isLocalhost(&address)) { + return osNetworkSystem_connectSocketImpl(env, clazz, fileDescriptor, + trafficClass, inetAddr, port); + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return -1; + } + + address.sin_port = htons(port); + + context = (jbyte *)env->GetPrimitiveArrayCritical(passContext, NULL); + + switch (step) { + case SOCKET_CONNECT_STEP_START: + result = sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_START, context); + break; + case SOCKET_CONNECT_STEP_CHECK: + result = sockConnectWithTimeout(handle, address, timeout, + SOCKET_STEP_CHECK, context); + break; + } + + env->ReleasePrimitiveArrayCritical(passContext, context, JNI_ABORT); + + if (0 == result) { + /* connected , so stop here */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, NULL); + } else if (result != SOCKERR_NOTCONNECTED) { + /* can not connect... */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, NULL); + if (result == SOCKERR_EACCES) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(result)); + } + } + + return result; +} + +static void osNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jint remotePort, jint timeout, + jint trafficClass, jobject inetAddr) { + // LOGD("ENTER connectStreamWithTimeoutSocketImpl"); + + int result = 0; + int handle; + struct sockaddr_in address; + jbyte *context = NULL; + int remainingTimeout = timeout; + int passedTimeout = 0; + int finishTime = 0; + int blocking = 0; + char hasTimeout = timeout > 0; + + /* if a timeout was specified calculate the finish time value */ + if (hasTimeout) { + finishTime = time_msec_clock() + (int) timeout; + } + + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } else { + result = inetAddressToSocketAddress(env, inetAddr, remotePort, + (struct sockaddr_in *) &address); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + // Check if we're using adb networking and redirect in case it is used. + if (useAdbNetworking && !isLocalhost(&address)) { + int retVal = osNetworkSystem_connectSocketImpl(env, clazz, + fileDescriptor, trafficClass, inetAddr, remotePort); + if (retVal != 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + } + return; + } + + /* + * we will be looping checking for when we are connected so allocate + * the descriptor sets that we will use + */ + context =(jbyte *) malloc(sizeof(struct selectFDSet)); + + if (NULL == context) { + throwSocketException(env, SOCKERR_NOBUFFERS); + return; + } + + result = sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_START, context); + if (0 == result) { + /* ok we connected right away so we are done */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, context); + goto bail; + } else if (result != SOCKERR_NOTCONNECTED) { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, + context); + /* we got an error other than NOTCONNECTED so we cannot continue */ + if (SOCKERR_EACCES == result) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + throwSocketException(env, result); + } + goto bail; + } + + while (SOCKERR_NOTCONNECTED == result) { + passedTimeout = remainingTimeout; + + /* + * ok now try and connect. Depending on the platform this may sleep + * for up to passedTimeout milliseconds + */ + result = sockConnectWithTimeout(handle, address, passedTimeout, + SOCKET_STEP_CHECK, context); + + /* + * now check if the socket is still connected. + * Do it here as some platforms seem to think they + * are connected if the socket is closed on them. + */ + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + throwSocketException(env, SOCKERR_BADSOCKET); + goto bail; + } + + /* + * check if we are now connected, + * if so we can finish the process and return + */ + if (0 == result) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + goto bail; + } + + /* + * if the error is SOCKERR_NOTCONNECTED then we have not yet + * connected and we may not be done yet + */ + if (SOCKERR_NOTCONNECTED == result) { + /* check if the timeout has expired */ + if (hasTimeout) { + remainingTimeout = finishTime - time_msec_clock(); + if (remainingTimeout <= 0) { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + jniThrowException(env, + "java/net/SocketTimeoutException", + netLookupErrorString(result)); + goto bail; + } + } else { + remainingTimeout = 100; + } + } else { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, remainingTimeout, + SOCKET_STEP_DONE, context); + if ((SOCKERR_CONNRESET == result) || + (SOCKERR_CONNECTION_REFUSED == result) || + (SOCKERR_ADDRNOTAVAIL == result) || + (SOCKERR_ADDRINUSE == result) || + (SOCKERR_ENETUNREACH == result)) { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(result)); + } else if (SOCKERR_EACCES == result) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + throwSocketException(env, result); + } + goto bail; + } + } + } + +bail: + + /* free the memory for the FD set */ + if (context != NULL) { + free(context); + } +} + +static jint osNetworkSystem_connectSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint trafficClass, jobject inetAddr, jint port) { + //LOGD("ENTER direct-call connectSocketImpl\n"); + + struct sockaddr_in address; + int ret; + int handle; + jbyteArray java_in_addr; + + memset(&address, 0, sizeof(address)); + + address.sin_family = AF_INET; + + ret = inetAddressToSocketAddress(env, inetAddr, port, + (struct sockaddr_in *) &address); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return ret; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return -1; + } + + address.sin_port = htons(port); + + if (useAdbNetworking && !isLocalhost(&address)) { + + // LOGD("+connect to address 0x%08x port %d (via adb)", + // address.sin_addr.s_addr, (int) port); + ret = adb_networking_connect_fd(handle, &address); + // LOGD("-connect ret %d errno %d (via adb)", ret, errno); + + } else { + + // call this method with a timeout of zero + osNetworkSystem_connectStreamWithTimeoutSocketImpl(env, clazz, + fileDescriptor, port, 0, trafficClass, inetAddr); + if (env->ExceptionOccurred() != 0) { + return -1; + } else { + return 0; + } + + } + + if (ret < 0) { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(convertError(errno))); + return ret; + } + + return ret; +} + +static void osNetworkSystem_socketBindImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint port, jobject inetAddress) { + // LOGD("ENTER socketBindImpl"); + + struct sockaddr_in sockaddress; + int ret; + int handle; + + ret = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddress); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); + + if (ret < 0) { + jniThrowException(env, "java/net/BindException", + netLookupErrorString(convertError(errno))); + return; + } +} + +static void osNetworkSystem_listenStreamSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint backlog) { + // LOGD("ENTER listenStreamSocketImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = listen(handle, backlog); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +static jint osNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor) { + // LOGD("ENTER availableStreamImpl"); + + int handle; + char message[BUFFERSIZE]; + + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + do { + result = selectWait(handle, 1, SELECT_READ_TYPE); + + if (SOCKERR_TIMEOUT == result) { + // The read operation timed out, so answer 0 bytes available + return 0; + } else if (SOCKERR_INTERRUPTED == result) { + continue; + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + return 0; + } + } while (SOCKERR_INTERRUPTED == result); + + result = recv(handle, (jbyte *) message, BUFFERSIZE, MSG_PEEK); + + if (0 > result) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + add_recv_stats(handle, result); + return result; +} + +static void osNetworkSystem_acceptSocketImpl(JNIEnv* env, jclass clazz, + jobject fdServer, jobject newSocket, jobject fdnewSocket, jint timeout) { + // LOGD("ENTER acceptSocketImpl"); + + union { + struct sockaddr address; + struct sockaddr_in in_address; + } sa; + + int ret; + int retFD; + int result; + int handle; + socklen_t addrlen; + + if (newSocket == NULL) { + throwNullPointerException(env); + return; + } + + result = pollSelectWait(env, fdServer, timeout, SELECT_READ_TYPE); + + if (0 > result) { + return; + } + + handle = jniGetFDFromFileDescriptor(env, fdServer); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + do { + addrlen = sizeof(sa); + ret = accept(handle, &(sa.address), &addrlen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } + + retFD = ret; + + /* For AF_INET / inetOrLocal == true only: put + * peer address and port in instance variables + * We don't bother for UNIX domain sockets, since most peers are + * anonymous anyway + */ + if (sa.address.sa_family == AF_INET) { + // inetOrLocal should also be true + + jobject inetAddress; + + inetAddress = structInToInetAddress(env, &(sa.in_address.sin_addr)); + + if (inetAddress == NULL) { + close(retFD); + newSocket = NULL; + return; + } + + env->SetObjectField(newSocket, + gCachedFields.socketimpl_address, inetAddress); + + env->SetIntField(newSocket, gCachedFields.socketimpl_port, + ntohs(sa.in_address.sin_port)); + } + + jniSetFileDescriptorOfFD(env, fdnewSocket, retFD); +} + +static jboolean osNetworkSystem_supportsUrgentDataImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor) { + // LOGD("ENTER supportsUrgentDataImpl"); + + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + return JNI_FALSE; + } + + return JNI_TRUE; +} + +static void osNetworkSystem_sendUrgentDataImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyte value) { + // LOGD("ENTER sendUrgentDataImpl"); + + int handle; + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + result = send(handle, (jbyte *) &value, 1, MSG_OOB); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +static void osNetworkSystem_connectDatagramImpl2(JNIEnv* env, jclass clazz, + jobject fd, jint port, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER connectDatagramImpl2"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + struct sockaddr_in sockAddr; + int ret; + + ret = inetAddressToSocketAddress(env, inetAddress, port, &sockAddr); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + log_socket_connect(handle, ntohl(sockAddr.sin_addr.s_addr), port); + int result = connect(handle, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +static void osNetworkSystem_disconnectDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd) { + // LOGD("ENTER disconnectDatagramImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + struct sockaddr_in *sockAddr; + socklen_t sockAddrLen = sizeof(struct sockaddr_in); + sockAddr = (struct sockaddr_in *) malloc(sockAddrLen); + memset(sockAddr, 0, sockAddrLen); + + sockAddr->sin_family = AF_UNSPEC; + int result = connect(handle, (struct sockaddr *)sockAddr, sockAddrLen); + free(sockAddr); + + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +static jboolean osNetworkSystem_socketBindImpl2(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint port, jboolean bindToDevice, + jobject inetAddress) { + // LOGD("ENTER socketBindImpl2"); + + struct sockaddr_in sockaddress; + int ret; + int handle; + + ret = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddress); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + jniThrowException(env, "java/net/BindException", netLookupErrorString(err)); + return 0; + } + + return 0; +} + +static jint osNetworkSystem_peekDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject sender, jint receiveTimeout) { + // LOGD("ENTER peekDatagramImpl"); + + int port = -1; + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + if (0> result) { + return (jint) 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in sockAddr; + socklen_t sockAddrLen = sizeof(sockAddr); + + int length = recvfrom(handle, NULL, 0, MSG_PEEK, + (struct sockaddr *)&sockAddr, &sockAddrLen); + + if (length < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + + if (socketAddressToInetAddress(env, &sockAddr, sender, &port) < 0) { + throwIOExceptionStr(env, "Address conversion failed"); + return -1; + } + add_recv_stats(handle, length); + return port; +} + +static jint osNetworkSystem_receiveDatagramDirectImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jint address, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveDatagramDirectImpl"); + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + if (0 > result) { + return (jint) 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in sockAddr; + socklen_t sockAddrLen = sizeof(sockAddr); + + int mode = peek ? MSG_PEEK : 0; + + int actualLength = recvfrom(handle, (char*)(address + offset), length, mode, + (struct sockaddr *)&sockAddr, &sockAddrLen); + + if (actualLength < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + + if (packet != NULL) { + int port = ntohs(sockAddr.sin_port); + jbyteArray addr = env->NewByteArray(sizeof(struct in_addr)); + if ((structInToJavaAddress(env, &sockAddr.sin_addr, addr)) < 0) { + jniThrowException(env, "java/net/SocketException", + "Could not set address of packet."); + return 0; + } + jobject sender = env->CallStaticObjectMethod( + gCachedFields.iaddr_class, gCachedFields.iaddr_getbyaddress, + addr); + env->SetObjectField(packet, gCachedFields.dpack_address, sender); + env->SetIntField(packet, gCachedFields.dpack_port, port); + env->SetIntField(packet, gCachedFields.dpack_length, actualLength); + } + add_recv_stats(handle, actualLength); + return actualLength; +} + +static jint osNetworkSystem_receiveDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jbyteArray data, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveDatagramImpl"); + + int localLength = (length < 65536) ? length : 65536; + jbyte *bytes = (jbyte*) malloc(localLength); + if (bytes == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for receiveDatagram"); + return 0; + } + + int actualLength = osNetworkSystem_receiveDatagramDirectImpl(env, clazz, fd, + packet, (jint)bytes, offset, localLength, receiveTimeout, peek); + + if (actualLength > 0) { + env->SetByteArrayRegion(data, offset, actualLength, bytes); + } + free(bytes); + + return actualLength; +} + +static jint osNetworkSystem_recvConnectedDatagramDirectImpl(JNIEnv* env, + jclass clazz, jobject fd, jobject packet, jint address, jint offset, + jint length, jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveConnectedDatagramDirectImpl"); + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + + if (0 > result) { + return 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + int mode = peek ? MSG_PEEK : 0; + + int actualLength = recvfrom(handle, + (char*)(address + offset), length, mode, NULL, NULL); + + if (actualLength < 0) { + jniThrowException(env, "java/net/PortUnreachableException", ""); + return 0; + } + + if ( packet != NULL) { + env->SetIntField(packet, gCachedFields.dpack_length, actualLength); + } + add_recv_stats(handle, actualLength); + return actualLength; +} + +static jint osNetworkSystem_recvConnectedDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jbyteArray data, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveConnectedDatagramImpl"); + + int localLength = (length < 65536) ? length : 65536; + jbyte *bytes = (jbyte*) malloc(localLength); + if (bytes == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for recvConnectedDatagram"); + return 0; + } + + int actualLength = osNetworkSystem_recvConnectedDatagramDirectImpl(env, + clazz, fd, packet, (jint)bytes, offset, localLength, + receiveTimeout, peek); + + if (actualLength > 0) { + env->SetByteArrayRegion(data, offset, actualLength, bytes); + } + free(bytes); + + return actualLength; +} + +static jint osNetworkSystem_sendDatagramDirectImpl(JNIEnv* env, jclass clazz, + jobject fd, jint address, jint offset, jint length, jint port, + jboolean bindToDevice, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER sendDatagramDirectImpl"); + + int result = 0; + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in receiver; + + if (inetAddressToSocketAddress(env, inetAddress, port, &receiver) < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = sendto(handle, (char*)(address + offset), length, SOCKET_NOFLAGS, + (struct sockaddr*)&receiver, sizeof(receiver)); + + if (result < 0) { + int err = convertError(errno); + if ((SOCKERR_CONNRESET == err) + || (SOCKERR_CONNECTION_REFUSED == err)) { + return 0; + } else { + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + } + add_send_stats(handle, result); + return result; +} + +static jint osNetworkSystem_sendDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, jint port, + jboolean bindToDevice, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER sendDatagramImpl"); + + jbyte *bytes = env->GetByteArrayElements(data, NULL); + int actualLength = osNetworkSystem_sendDatagramDirectImpl(env, clazz, fd, + (jint)bytes, offset, length, port, bindToDevice, trafficClass, + inetAddress); + env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + + return actualLength; +} + +static jint osNetworkSystem_sendConnectedDatagramDirectImpl(JNIEnv* env, + jclass clazz, jobject fd, jint address, jint offset, jint length, + jboolean bindToDevice) { + // LOGD("ENTER sendConnectedDatagramDirectImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + int result = send(handle, (char*)(address + offset), length, 0); + + if (result < 0) { + int err = convertError(errno); + if ((SOCKERR_CONNRESET == err) || (SOCKERR_CONNECTION_REFUSED == err)) { + return 0; + } else { + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + } + add_send_stats(handle, length); + return result; +} + +static jint osNetworkSystem_sendConnectedDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, + jboolean bindToDevice) { + // LOGD("ENTER sendConnectedDatagramImpl"); + + jbyte *bytes = env->GetByteArrayElements(data, NULL); + int actualLength = osNetworkSystem_sendConnectedDatagramDirectImpl(env, + clazz, fd, (jint)bytes, offset, length, bindToDevice); + env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + + return actualLength; +} + +static void osNetworkSystem_createServerStreamSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createServerStreamSocketImpl"); + + if (fileDescriptor == NULL) { + throwNullPointerException(env); + return; + } + + int handle = socket(PF_INET, SOCK_STREAM, 0); + + if (handle < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, handle); + + int value = 1; + + setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); +} + +static void osNetworkSystem_createMulticastSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createMulticastSocketImpl"); + + int handle = socket(PF_INET, SOCK_DGRAM, 0); + + if (handle < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, handle); + + int value = 1; + + // setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(jbyte)); + setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); +} + +/* + * @param timeout in milliseconds. If zero, block until data received + */ +static jint osNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count, + jint timeout) { + // LOGD("ENTER receiveStreamImpl"); + + int result; + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + // Cap read length to available buf size + int spaceAvailable = env->GetArrayLength(data) - offset; + int localCount = count < spaceAvailable? count : spaceAvailable; + + jboolean isCopy; + jbyte *body = env->GetByteArrayElements(data, &isCopy); + + // set timeout + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, + sizeof(struct timeval)); + + do { + result = recv(handle, body + offset, localCount, SOCKET_NOFLAGS); + } while (result < 0 && errno == EINTR); + + env->ReleaseByteArrayElements(data, body, 0); + + /* + * If no bytes are read, return -1 to signal 'endOfFile' + * to the Java input stream + */ + if (0 < result) { + add_recv_stats(handle, result); + return result; + } else if (0 == result) { + return -1; + } else { + // If EAGAIN or EWOULDBLOCK, read timed out + if (errno == EAGAIN || errno == EWOULDBLOCK) { + jniThrowException(env, "java/net/SocketTimeoutException", + netLookupErrorString(SOCKERR_TIMEOUT)); + } else { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } + return 0; + } +} + +static jint osNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count) { + // LOGD("ENTER sendStreamImpl"); + + int handle = 0; + int result = 0, sent = 0; + + jboolean isCopy; + jbyte *message = env->GetByteArrayElements(data, &isCopy); + + // Cap write length to available buf size + int spaceAvailable = env->GetArrayLength(data) - offset; + if (count > spaceAvailable) count = spaceAvailable; + + while (sent < count) { + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, + sent == 0 ? SOCKERR_BADSOCKET : SOCKERR_INTERRUPTED); + env->ReleaseByteArrayElements(data, message, 0); + return 0; + } + + // LOGD("before select %d", count); + selectWait(handle, SEND_RETRY_TIME, SELECT_WRITE_TYPE); + result = send(handle, (jbyte *)message + offset + sent, + (int) count - sent, SOCKET_NOFLAGS); + + if (result < 0) { + result = errno; + if (result == EAGAIN ||result == EWOULDBLOCK) { + // LOGD("write blocked %d", sent); + continue; + } + env->ReleaseByteArrayElements(data, message, 0); + int err = convertError(result); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + sent += result; + } + + env->ReleaseByteArrayElements(data, message, 0); + add_send_stats(handle, sent); + return sent; +} + +static void osNetworkSystem_shutdownInputImpl(JNIEnv* env, jobject obj, + jobject fileDescriptor) { + // LOGD("ENTER shutdownInputImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = shutdown(handle, SHUT_RD); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +static void osNetworkSystem_shutdownOutputImpl(JNIEnv* env, jobject obj, + jobject fileDescriptor) { + // LOGD("ENTER shutdownOutputImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + return; + } + + ret = shutdown(handle, SHUT_WR); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, jint port, + jobject inetAddress) { + // LOGD("ENTER sendDatagramImpl2"); + + jbyte *message; + jbyte nhostAddrBytes[4]; + unsigned short nPort; + int result = 0, sent = 0; + int handle = 0; + struct sockaddr_in sockaddrP; + + if (inetAddress != NULL) { + + result = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + } + + message = (jbyte*) malloc(length * sizeof(jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for readSocket"); + return 0; + } + + env->GetByteArrayRegion(data, offset, length, message); + + while (sent < length) { + handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, + sent == 0 ? SOCKERR_BADSOCKET : SOCKERR_INTERRUPTED); + free(message); + return 0; + } + + result = sendto(handle, (char *) (message + sent), + (int) (length - sent), SOCKET_NOFLAGS, + (struct sockaddr *) &sockaddrP, sizeof(sockaddrP)); + + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + free(message); + return 0; + } + + sent += result; + } + + free(message); + add_send_stats(handle, sent); + return sent; +} + +static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, + jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC, + jint countWriteC, jintArray outFlags, jlong timeout) { + // LOGD("ENTER selectImpl"); + + struct timeval timeP; + int result = 0; + int size = 0; + jobject gotFD; + fd_set *fdset_read,*fdset_write; + int handle; + jboolean isCopy ; + jint *flagArray; + int val; + unsigned int time_sec = (unsigned int)timeout/1000; + unsigned int time_msec = (unsigned int)(timeout%1000); + + fdset_read = (fd_set *)malloc(sizeof(fd_set)); + fdset_write = (fd_set *)malloc(sizeof(fd_set)); + + FD_ZERO(fdset_read); + FD_ZERO(fdset_write); + + for (val = 0; val<countReadC; val++) { + + gotFD = env->GetObjectArrayElement(readFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + FD_SET(handle, fdset_read); + + if (0 > (size - handle)) { + size = handle; + } + } + + for (val = 0; val<countWriteC; val++) { + + gotFD = env->GetObjectArrayElement(writeFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + FD_SET(handle, fdset_write); + + if (0 > (size - handle)) { + size = handle; + } + } + + /* the size is the max_fd + 1 */ + size =size + 1; + + if (0 > size) { + result = SOCKERR_FDSET_SIZEBAD; + } else { + /* only set when timeout >= 0 (non-block)*/ + if (0 <= timeout) { + + timeP.tv_sec = time_sec; + timeP.tv_usec = time_msec*1000; + + result = sockSelect(size, fdset_read, fdset_write, NULL, &timeP); + + } else { + result = sockSelect(size, fdset_read, fdset_write, NULL, NULL); + } + } + + if (0 < result) { + /*output the result to a int array*/ + flagArray = env->GetIntArrayElements(outFlags, &isCopy); + + for (val=0; val<countReadC; val++) { + gotFD = env->GetObjectArrayElement(readFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + if (FD_ISSET(handle,fdset_read)) { + flagArray[val] = SOCKET_OP_READ; + } else { + flagArray[val] = SOCKET_OP_NONE; + } + } + + for (val=0; val<countWriteC; val++) { + + gotFD = env->GetObjectArrayElement(writeFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + if (FD_ISSET(handle,fdset_write)) { + flagArray[val+countReadC] = SOCKET_OP_WRITE; + } else { + flagArray[val+countReadC] = SOCKET_OP_NONE; + } + } + + env->ReleaseIntArrayElements(outFlags, flagArray, 0); + } + + free(fdset_write); + free(fdset_read); + + /* return both correct and error result, let java handle the exception*/ + return result; +} + +static jobject osNetworkSystem_getSocketLocalAddressImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv6Addresses) { + // LOGD("ENTER getSocketLocalAddressImpl"); + + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + + memset(&addr, 0, addrLen); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + int result; + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_UNKNOWNSOCKET); + return NULL; + } + + result = getsockname(handle, (struct sockaddr *)&addr, &addrLen); + + // Spec says ignore all errors + + return structInToInetAddress(env, &(addr.sin_addr)); + +} + +static jint osNetworkSystem_getSocketLocalPortImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv6Addresses) { + // LOGD("ENTER getSocketLocalPortImpl"); + + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + int result; + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_UNKNOWNSOCKET); + return 0; + } + + result = getsockname(handle, (struct sockaddr *)&addr, &addrLen); + + if (0 != result) { + // The java spec does not indicate any exceptions on this call + return 0; + } else { + return ntohs(addr.sin_port); + } +} + +static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint anOption) { + // LOGD("ENTER getSocketOptionImpl"); + + int handle; + int intValue = 0; + socklen_t intSize = sizeof(int); + unsigned char byteValue = 0; + socklen_t byteSize = sizeof(unsigned char); + int result; + struct sockaddr_in sockVal; + socklen_t sockSize = sizeof(sockVal); + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return NULL; + } + + switch ((int) anOption & 0xffff) { + case JAVASOCKOPT_SO_LINGER: { + struct linger lingr; + socklen_t size = sizeof(struct linger); + result = getsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, &size); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + if (!lingr.l_onoff) { + intValue = -1; + } else { + intValue = lingr.l_linger; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_TCP_NODELAY: { + if ((anOption >> 16) & BROKEN_TCP_NODELAY) { + return NULL; + } + result = getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_MCAST_TTL: { + if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { + return newJavaLangByte(env, 0); + } + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteValue, &byteSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangByte(env, (jbyte)(byteValue & 0xFF)); + } + case JAVASOCKOPT_MCAST_INTERFACE: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return NULL; + } + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, &sockSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return structInToInetAddress(env, &(sockVal.sin_addr)); + } + case JAVASOCKOPT_SO_SNDBUF: { + result = getsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_RCVBUF: { + result = getsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_BROADCAST: { + result = getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_REUSEADDR: { + result = getsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_KEEPALIVE: { + result = getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_OOBINLINE: { + result = getsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_IP_MULTICAST_LOOP: { + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_IP_TOS: { + result = getsockopt(handle, IPPROTO_IP, IP_TOS, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_RCVTIMEOUT: { + struct timeval timeout; + socklen_t size = sizeof(timeout); + result = getsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, &size); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, timeout.tv_sec * 1000 + timeout.tv_usec/1000); + } + default: { + throwSocketException(env, SOCKERR_OPTUNSUPP); + return NULL; + } + } + +} + +static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint anOption, jobject optVal) { + // LOGD("ENTER setSocketOptionImpl"); + + int handle, result; + int intVal, intSize = sizeof(int); + unsigned char byteVal, byteSize = sizeof(unsigned char); + struct sockaddr_in sockVal; + int sockSize = sizeof(sockVal); + + if (env->IsInstanceOf(optVal, gCachedFields.integer_class)) { + intVal = (int) env->GetIntField(optVal, gCachedFields.integer_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.boolean_class)) { + intVal = (int) env->GetBooleanField(optVal, gCachedFields.boolean_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.byte_class)) { + byteVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) { + if (inetAddressToSocketAddress(env, optVal, 0, &sockVal) < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class)) { + // we'll use optVal directly + } else { + throwSocketException(env, SOCKERR_OPTUNSUPP); + return; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + switch ((int) anOption & 0xffff) { + case JAVASOCKOPT_SO_LINGER: { + struct linger lingr; + lingr.l_onoff = intVal > 0 ? 1 : 0; + lingr.l_linger = intVal; + result = setsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, + sizeof(struct linger)); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_TCP_NODELAY: { + if ((anOption >> 16) & BROKEN_TCP_NODELAY) { + return; + } + result = setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_MCAST_TTL: { + if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { + return; + } + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteVal, byteSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP: { + mcastAddDropMembership(env, handle, optVal, + (anOption >> 16) & BROKEN_MULTICAST_IF, IP_ADD_MEMBERSHIP); + return; + } + + case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP: { + mcastAddDropMembership(env, handle, optVal, + (anOption >> 16) & BROKEN_MULTICAST_IF, IP_DROP_MEMBERSHIP); + return; + } + + case JAVASOCKOPT_MCAST_INTERFACE: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return; + } + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, sockSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_SNDBUF: { + result = setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_RCVBUF: { + result = setsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_BROADCAST: { + result = setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_REUSEADDR: { + result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + case JAVASOCKOPT_SO_KEEPALIVE: { + result = setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_OOBINLINE: { + result = setsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_IP_MULTICAST_LOOP: { + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_IP_TOS: { + result = setsockopt(handle, IPPROTO_IP, IP_TOS, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_REUSEADDR_AND_REUSEPORT: { + // SO_REUSEPORT doesn't need to get set on this System + result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_RCVTIMEOUT: { + struct timeval timeout; + timeout.tv_sec = intVal / 1000; + timeout.tv_usec = (intVal % 1000) * 1000; + result = setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(struct timeval)); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + default: { + throwSocketException(env, SOCKERR_OPTUNSUPP); + } + } +} + +static jint osNetworkSystem_getSocketFlagsImpl(JNIEnv* env, jclass clazz) { + // LOGD("ENTER getSocketFlagsImpl"); + + // Not implemented by harmony + return 0; +} + +static void osNetworkSystem_socketCloseImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor) { + // LOGD("ENTER socketCloseImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + log_socket_close(handle, SOCKET_CLOSE_LOCAL); + + jniSetFileDescriptorOfFD(env, fileDescriptor, -1); + + close(handle); +} + +static jobject osNetworkSystem_getHostByAddrImpl(JNIEnv* env, jclass clazz, + jbyteArray addrStr) { + // LOGD("ENTER getHostByAddrImpl"); + + if (addrStr == NULL) { + throwNullPointerException(env); + return JNI_FALSE; + } + + jstring address = (jstring)newJavaLangString(env, addrStr); + jstring result; + const char* addr = env->GetStringUTFChars(address, NULL); + + struct hostent* ent = gethostbyaddr(addr, strlen(addr), AF_INET); + + if (ent != NULL && ent->h_name != NULL) { + result = env->NewStringUTF(ent->h_name); + } else { + result = NULL; + } + + env->ReleaseStringUTFChars(address, addr); + + return result; +} + +static jobject osNetworkSystem_getHostByNameImpl(JNIEnv* env, jclass clazz, + jstring nameStr, jboolean preferIPv6Addresses) { + // LOGD("ENTER getHostByNameImpl"); + + if (nameStr == NULL) { + throwNullPointerException(env); + return NULL; + } + + const char* name = env->GetStringUTFChars(nameStr, NULL); + + if (useAdbNetworking) { + + union { + struct in_addr a; + jbyte j[4]; + } outaddr; + + // LOGD("ADB networking: +gethostbyname '%s'", name); + int err; + err = adb_networking_gethostbyname(name, &(outaddr.a)); + + env->ReleaseStringUTFChars(nameStr, name); +#if 0 + LOGD("ADB networking: -gethostbyname err %d addr 0x%08x %u.%u.%u.%u", + err, (unsigned int)outaddr.a.s_addr, + outaddr.j[0],outaddr.j[1], + outaddr.j[2],outaddr.j[3]); +#endif + + if (err < 0) { + return NULL; + } else { + jbyteArray addr = env->NewByteArray(4); + env->SetByteArrayRegion(addr, 0, 4, outaddr.j); + return addr; + } + } else { + + // normal case...no adb networking + struct hostent* ent = gethostbyname(name); + + env->ReleaseStringUTFChars(nameStr, name); + + if (ent != NULL && ent->h_length > 0) { + jbyteArray addr = env->NewByteArray(4); + jbyte v[4]; + memcpy(v, ent->h_addr, 4); + env->SetByteArrayRegion(addr, 0, 4, v); + return addr; + } else { + return NULL; + } + } +} + +static void osNetworkSystem_setInetAddressImpl(JNIEnv* env, jobject obj, + jobject sender, jbyteArray address) { + // LOGD("ENTER setInetAddressImpl"); + + env->SetObjectField(sender, gCachedFields.iaddr_ipaddress, address); +} + +static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { + // LOGD("ENTER inheritedChannelImpl"); + + int socket = 0; + int opt; + socklen_t length = sizeof(opt); + int socket_type; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + jclass channel_class, socketaddr_class, serverSocket_class, socketImpl_class; + jobject channel_object = NULL, socketaddr_object, serverSocket_object; + jobject fd_object, addr_object, localAddr_object, socketImpl_object; + jfieldID port_field, socketaddr_field, bound_field, fd_field; + jfieldID serverSocket_field, socketImpl_field, addr_field, localAddr_field; + jmethodID channel_new; + jbyteArray addr_array; + struct sockaddr_in *sock; + jbyte * address; + jbyte * localAddr; + jboolean jtrue = JNI_TRUE; + + if (0 != getsockopt(socket, SOL_SOCKET, SO_TYPE, &opt, &length)) { + return NULL; + } + if (SOCK_STREAM !=opt && SOCK_DGRAM !=opt) { + return NULL; + } + socket_type = opt; + + length = sizeof(struct sockaddr); + if (0 != getsockname(socket, (struct sockaddr *)&local_addr, &length)) { + return NULL; + } else { + if (AF_INET != local_addr.sin_family || length != sizeof(struct sockaddr)) { + return NULL; + } + localAddr = (jbyte*) malloc(sizeof(jbyte)*4); + if (NULL == localAddr) { + return NULL; + } + memcpy (localAddr, &(local_addr.sin_addr.s_addr), 4); + } + if (0 != getpeername(socket, (struct sockaddr *)&remote_addr, &length)) { + remote_addr.sin_port = 0; + remote_addr.sin_addr.s_addr = 0; + address = (jbyte*) malloc(sizeof(jbyte)*4); + bzero(address, sizeof(jbyte)*4); + } else { + if (AF_INET != remote_addr.sin_family + || length != sizeof(struct sockaddr)) { + return NULL; + } + address = (jbyte*) malloc(sizeof(jbyte)*4); + memcpy (address, &(remote_addr.sin_addr.s_addr), 4); + } + + // analysis end, begin pack to java + if (SOCK_STREAM == opt) { + if (remote_addr.sin_port!=0) { + //socket + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/SocketChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "<init>", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + // new and set FileDescript + + fd_field = env->GetFieldID(channel_class, "fd", + "java/io/FielDescriptor"); + fd_object = env->GetObjectField(channel_object, fd_field); + if (NULL == fd_object) { + goto clean; + } + + jniSetFileDescriptorOfFD(env, fd_object, socket); + + // local port + port_field = env->GetFieldID(channel_class, "localPort", "I"); + env->SetIntField(channel_object, port_field, + ntohs(local_addr.sin_port)); + + // new and set remote addr + addr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == addr_object) { + goto clean; + } + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + if (NULL == socketaddr_object) { + goto clean; + } + addr_field = env->GetFieldID(socketaddr_class, "addr", + "Ljava/net/InetAddress;"); + env->SetObjectField(socketaddr_object, addr_field, addr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); + env->SetObjectField(addr_object, gCachedFields.iaddr_ipaddress, + addr_array); + + // localAddr + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + + localAddr_field = env->GetFieldID(channel_class, "localAddress", + "Ljava/net/InetAddress;"); + localAddr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + jfieldID socketaddr_field = env->GetFieldID(channel_class, + "connectAddress", "Ljava/net/InetSocketAddress;"); + jobject socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + env->SetObjectField(socketaddr_object, localAddr_field, + localAddr_object); + if (NULL == localAddr_object) { + goto clean; + } + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, localAddr); + env->SetObjectField(localAddr_object, gCachedFields.iaddr_ipaddress, + addr_array); + + + // set port + port_field = env->GetFieldID(socketaddr_class, "port", "I"); + env->SetIntField(socketaddr_object, port_field, + ntohs(remote_addr.sin_port)); + + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + } + + } else { + //serverSocket + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/ServerSocketChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "<init>", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + + serverSocket_field = env->GetFieldID(channel_class, "socket", + "Ljava/net/ServerSocket;"); + serverSocket_class = env->FindClass("Ljava/net/ServerSocket;"); + serverSocket_object = env->GetObjectField(channel_object, + serverSocket_field); + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + bound_field = env->GetFieldID(serverSocket_class, "isBound", "Z"); + env->SetBooleanField(serverSocket_object, bound_field, jtrue); + } + // localAddr + socketImpl_class = env->FindClass("java/net/SocketImpl"); + socketImpl_field = env->GetFieldID(channel_class, "impl", + "Ljava/net/SocketImpl;"); + socketImpl_object = env->GetObjectField(channel_object, + socketImpl_field); + if (NULL == socketImpl_object) { + goto clean; + } + + localAddr_field = env->GetFieldID(channel_class, "localAddress", + "Ljava/net/InetAddress;"); + localAddr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == localAddr_object) { + goto clean; + } + env->SetObjectField(socketImpl_object, localAddr_field, + localAddr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, localAddr); + env->SetObjectField(localAddr_object, + gCachedFields.iaddr_ipaddress, addr_array); + + // set port + port_field = env->GetFieldID(socketImpl_class, "localport", "I"); + env->SetIntField(socketImpl_object, port_field, + ntohs(local_addr.sin_port)); + } + } else { + //Datagram Socket + // new DatagramChannel + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/DatagramChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "<init>", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + + // new and set FileDescript + fd_field = env->GetFieldID(channel_class, "fd", "java/io/FileDescriptor"); + fd_object = env->GetObjectField(channel_object, fd_field); + if (NULL == fd_object) { + goto clean; + } + + jniSetFileDescriptorOfFD(env, fd_object, socket); + + port_field = env->GetFieldID(channel_class, "localPort", "I"); + env->SetIntField(channel_object, port_field, ntohs(local_addr.sin_port)); + + // new and set remote addr + addr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == addr_object) { + goto clean; + } + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, socketaddr_field); + if (NULL == socketaddr_object) { + goto clean; + } + addr_field = env->GetFieldID(socketaddr_class, "addr", + "Ljava/net/InetAddress;"); + env->SetObjectField(socketaddr_object, addr_field, addr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); + env->SetObjectField(addr_object, gCachedFields.iaddr_ipaddress, addr_array); + + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + } + } +clean: + free(address); + free(localAddr); + return channel_object; +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "oneTimeInitializationImpl", "(Z)V", (void*) osNetworkSystem_oneTimeInitializationImpl }, + { "createSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createSocketImpl }, + { "createDatagramSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createDatagramSocketImpl }, + { "readSocketImpl", "(Ljava/io/FileDescriptor;[BIII)I", (void*) osNetworkSystem_readSocketImpl }, + { "readSocketDirectImpl", "(Ljava/io/FileDescriptor;IIII)I", (void*) osNetworkSystem_readSocketDirectImpl }, + { "writeSocketImpl", "(Ljava/io/FileDescriptor;[BII)I", (void*) osNetworkSystem_writeSocketImpl }, + { "writeSocketDirectImpl", "(Ljava/io/FileDescriptor;III)I", (void*) osNetworkSystem_writeSocketDirectImpl }, + { "setNonBlockingImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_setNonBlockingImpl }, + { "connectSocketImpl", "(Ljava/io/FileDescriptor;ILjava/net/InetAddress;I)I", (void*) osNetworkSystem_connectSocketImpl }, + { "connectWithTimeoutSocketImpl", "(Ljava/io/FileDescriptor;IILjava/net/InetAddress;II[B)I", (void*) osNetworkSystem_connectWithTimeoutSocketImpl }, + { "connectStreamWithTimeoutSocketImpl","(Ljava/io/FileDescriptor;IIILjava/net/InetAddress;)V", (void*) osNetworkSystem_connectStreamWithTimeoutSocketImpl }, + { "socketBindImpl", "(Ljava/io/FileDescriptor;ILjava/net/InetAddress;)V", (void*) osNetworkSystem_socketBindImpl }, + { "listenStreamSocketImpl", "(Ljava/io/FileDescriptor;I)V", (void*) osNetworkSystem_listenStreamSocketImpl }, + { "availableStreamImpl", "(Ljava/io/FileDescriptor;)I", (void*) osNetworkSystem_availableStreamImpl }, + { "acceptSocketImpl", "(Ljava/io/FileDescriptor;Ljava/net/SocketImpl;Ljava/io/FileDescriptor;I)V",(void*) osNetworkSystem_acceptSocketImpl }, + { "supportsUrgentDataImpl", "(Ljava/io/FileDescriptor;)Z", (void*) osNetworkSystem_supportsUrgentDataImpl }, + { "sendUrgentDataImpl", "(Ljava/io/FileDescriptor;B)V", (void*) osNetworkSystem_sendUrgentDataImpl }, + { "connectDatagramImpl2", "(Ljava/io/FileDescriptor;IILjava/net/InetAddress;)V", (void*) osNetworkSystem_connectDatagramImpl2 }, + { "disconnectDatagramImpl", "(Ljava/io/FileDescriptor;)V", (void*) osNetworkSystem_disconnectDatagramImpl }, + { "socketBindImpl2", "(Ljava/io/FileDescriptor;IZLjava/net/InetAddress;)Z", (void*) osNetworkSystem_socketBindImpl2 }, + { "peekDatagramImpl", "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)I", (void*) osNetworkSystem_peekDatagramImpl }, + { "receiveDatagramImpl", "(Ljava/io/FileDescriptor;Ljava/net/DatagramPacket;[BIIIZ)I", (void*) osNetworkSystem_receiveDatagramImpl }, + { "receiveDatagramDirectImpl", "(Ljava/io/FileDescriptor;Ljava/net/DatagramPacket;IIIIZ)I", (void*) osNetworkSystem_receiveDatagramDirectImpl }, + { "recvConnectedDatagramImpl", "(Ljava/io/FileDescriptor;Ljava/net/DatagramPacket;[BIIIZ)I", (void*) osNetworkSystem_recvConnectedDatagramImpl }, + { "recvConnectedDatagramDirectImpl", "(Ljava/io/FileDescriptor;Ljava/net/DatagramPacket;IIIIZ)I", (void*) osNetworkSystem_recvConnectedDatagramDirectImpl }, + { "sendDatagramImpl", "(Ljava/io/FileDescriptor;[BIIIZILjava/net/InetAddress;)I", (void*) osNetworkSystem_sendDatagramImpl }, + { "sendDatagramDirectImpl", "(Ljava/io/FileDescriptor;IIIIZILjava/net/InetAddress;)I", (void*) osNetworkSystem_sendDatagramDirectImpl }, + { "sendConnectedDatagramImpl", "(Ljava/io/FileDescriptor;[BIIZ)I", (void*) osNetworkSystem_sendConnectedDatagramImpl }, + { "sendConnectedDatagramDirectImpl", "(Ljava/io/FileDescriptor;IIIZ)I", (void*) osNetworkSystem_sendConnectedDatagramDirectImpl }, + { "createServerStreamSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createServerStreamSocketImpl }, + { "createMulticastSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createMulticastSocketImpl }, + { "receiveStreamImpl", "(Ljava/io/FileDescriptor;[BIII)I", (void*) osNetworkSystem_receiveStreamImpl }, + { "sendStreamImpl", "(Ljava/io/FileDescriptor;[BII)I", (void*) osNetworkSystem_sendStreamImpl }, + { "shutdownInputImpl", "(Ljava/io/FileDescriptor;)V", (void*) osNetworkSystem_shutdownInputImpl }, + { "shutdownOutputImpl", "(Ljava/io/FileDescriptor;)V", (void*) osNetworkSystem_shutdownOutputImpl }, + { "sendDatagramImpl2", "(Ljava/io/FileDescriptor;[BIIILjava/net/InetAddress;)I", (void*) osNetworkSystem_sendDatagramImpl2 }, + { "selectImpl", "([Ljava/io/FileDescriptor;[Ljava/io/FileDescriptor;II[IJ)I", (void*) osNetworkSystem_selectImpl }, + { "getSocketLocalAddressImpl", "(Ljava/io/FileDescriptor;Z)Ljava/net/InetAddress;", (void*) osNetworkSystem_getSocketLocalAddressImpl }, + { "getSocketLocalPortImpl", "(Ljava/io/FileDescriptor;Z)I", (void*) osNetworkSystem_getSocketLocalPortImpl }, + { "getSocketOptionImpl", "(Ljava/io/FileDescriptor;I)Ljava/lang/Object;", (void*) osNetworkSystem_getSocketOptionImpl }, + { "setSocketOptionImpl", "(Ljava/io/FileDescriptor;ILjava/lang/Object;)V", (void*) osNetworkSystem_setSocketOptionImpl }, + { "getSocketFlagsImpl", "()I", (void*) osNetworkSystem_getSocketFlagsImpl }, + { "socketCloseImpl", "(Ljava/io/FileDescriptor;)V", (void*) osNetworkSystem_socketCloseImpl }, + { "getHostByAddrImpl", "([B)Ljava/net/InetAddress;", (void*) osNetworkSystem_getHostByAddrImpl }, + { "getHostByNameImpl", "(Ljava/lang/String;Z)Ljava/net/InetAddress;", (void*) osNetworkSystem_getHostByNameImpl }, + { "setInetAddressImpl", "(Ljava/net/InetAddress;[B)V", (void*) osNetworkSystem_setInetAddressImpl }, + { "inheritedChannelImpl", "()Ljava/nio/channels/Channel;", (void*) osNetworkSystem_inheritedChannelImpl }, +}; + +int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env) { + return jniRegisterNativeMethods(env, + "org/apache/harmony/luni/platform/OSNetworkSystem", + gMethods, + NELEM(gMethods)); +} diff --git a/luni/src/main/native/org_apache_harmony_luni_util_NumberConvert.c b/luni/src/main/native/org_apache_harmony_luni_util_NumberConvert.c new file mode 100644 index 0000000..70761fb --- /dev/null +++ b/luni/src/main/native/org_apache_harmony_luni_util_NumberConvert.c @@ -0,0 +1,291 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> +#include <math.h> +#include <stdlib.h> + +#include "JNIHelp.h" +#include "cbigint.h" + +#if defined(LINUX) || defined(FREEBSD) +#define USE_LL +#endif + +JNIEXPORT void JNICALL +Java_org_apache_harmony_luni_util_NumberConverter_bigIntDigitGeneratorInstImpl (JNIEnv * + env, + jobject + inst, + jlong f, + jint e, + jboolean + isDenormalized, + jboolean + mantissaIsZero, + jint p); + + +#define INV_LOG_OF_TEN_BASE_2 (0.30102999566398114) /* Local */ +#define ERROR_OCCURED(x) (HIGH_I32_FROM_VAR(x) < 0) /* Local */ + +/*NB the Number converter methods are synchronized so it is possible to + *have global data for use by bigIntDigitGenerator */ +#define RM_SIZE 21 /* Local. */ +#define STemp_SIZE 22 /* Local. */ + +#if defined(WIN32) +#pragma optimize("",on) /*restore optimizations */ +#endif + +/* The algorithm for this particular function can be found in: + * + * Printing Floating-Point Numbers Quickly and Accurately, Robert + * G. Burger, and R. Kent Dybvig, Programming Language Design and + * Implementation (PLDI) 1996, pp.108-116. + * + * The previous implementation of this function combined m+ and m- into + * one single M which caused some inaccuracy of the last digit. The + * particular case below shows this inaccuracy: + * + * System.out.println(new Double((1.234123412431233E107)).toString()); + * System.out.println(new Double((1.2341234124312331E107)).toString()); + * System.out.println(new Double((1.2341234124312332E107)).toString()); + * + * outputs the following: + * + * 1.234123412431233E107 + * 1.234123412431233E107 + * 1.234123412431233E107 + * + * instead of: + * + * 1.234123412431233E107 + * 1.2341234124312331E107 + * 1.2341234124312331E107 + * + */ +JNIEXPORT void JNICALL +Java_org_apache_harmony_luni_util_NumberConverter_bigIntDigitGeneratorInstImpl (JNIEnv * + env, + jobject + inst, + jlong f, + jint e, + jboolean + isDenormalized, + jboolean + mantissaIsZero, + jint p) +{ + int RLength, SLength, TempLength, mplus_Length, mminus_Length; + int high, low, i; + jint k, firstK, U; + jint getCount, setCount; + jint *uArray; + + jclass clazz; + jfieldID fid; + jintArray uArrayObject; + + U_64 R[RM_SIZE], S[STemp_SIZE], mplus[RM_SIZE], mminus[RM_SIZE], + Temp[STemp_SIZE]; + + memset (R , 0, RM_SIZE * sizeof (U_64)); + memset (S , 0, STemp_SIZE * sizeof (U_64)); + memset (mplus , 0, RM_SIZE * sizeof (U_64)); + memset (mminus, 0, RM_SIZE * sizeof (U_64)); + memset (Temp , 0, STemp_SIZE * sizeof (U_64)); + + if (e >= 0) + { + *R = f; + *mplus = *mminus = 1; + simpleShiftLeftHighPrecision (mminus, RM_SIZE, e); + if (f != (2 << (p - 1))) + { + simpleShiftLeftHighPrecision (R, RM_SIZE, e + 1); + *S = 2; + /* + * m+ = m+ << e results in 1.0e23 to be printed as + * 0.9999999999999999E23 + * m+ = m+ << e+1 results in 1.0e23 to be printed as + * 1.0e23 (caused too much rounding) + * 470fffffffffffff = 2.0769187434139308E34 + * 4710000000000000 = 2.076918743413931E34 + */ + simpleShiftLeftHighPrecision (mplus, RM_SIZE, e); + } + else + { + simpleShiftLeftHighPrecision (R, RM_SIZE, e + 2); + *S = 4; + simpleShiftLeftHighPrecision (mplus, RM_SIZE, e + 1); + } + } + else + { + if (isDenormalized || (f != (2 << (p - 1)))) + { + *R = f << 1; + *S = 1; + simpleShiftLeftHighPrecision (S, STemp_SIZE, 1 - e); + *mplus = *mminus = 1; + } + else + { + *R = f << 2; + *S = 1; + simpleShiftLeftHighPrecision (S, STemp_SIZE, 2 - e); + *mplus = 2; + *mminus = 1; + } + } + + k = (int) ceil ((e + p - 1) * INV_LOG_OF_TEN_BASE_2 - 1e-10); + + if (k > 0) + { + timesTenToTheEHighPrecision (S, STemp_SIZE, k); + } + else + { + timesTenToTheEHighPrecision (R , RM_SIZE, -k); + timesTenToTheEHighPrecision (mplus , RM_SIZE, -k); + timesTenToTheEHighPrecision (mminus, RM_SIZE, -k); + } + + RLength = mplus_Length = mminus_Length = RM_SIZE; + SLength = TempLength = STemp_SIZE; + + memset (Temp + RM_SIZE, 0, (STemp_SIZE - RM_SIZE) * sizeof (U_64)); + memcpy (Temp, R, RM_SIZE * sizeof (U_64)); + + while (RLength > 1 && R[RLength - 1] == 0) + --RLength; + while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0) + --mplus_Length; + while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0) + --mminus_Length; + while (SLength > 1 && S[SLength - 1] == 0) + --SLength; + TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1; + addHighPrecision (Temp, TempLength, mplus, mplus_Length); + + if (compareHighPrecision (Temp, TempLength, S, SLength) >= 0) + { + firstK = k; + } + else + { + firstK = k - 1; + simpleAppendDecimalDigitHighPrecision (R , ++RLength , 0); + simpleAppendDecimalDigitHighPrecision (mplus , ++mplus_Length , 0); + simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0); + while (RLength > 1 && R[RLength - 1] == 0) + --RLength; + while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0) + --mplus_Length; + while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0) + --mminus_Length; + } + + clazz = (*env)->GetObjectClass (env, inst); + fid = (*env)->GetFieldID (env, clazz, "uArray", "[I"); + uArrayObject = (jintArray) (*env)->GetObjectField (env, inst, fid); + uArray = (*env)->GetIntArrayElements (env, uArrayObject, 0); + + getCount = setCount = 0; + do + { + U = 0; + for (i = 3; i >= 0; --i) + { + TempLength = SLength + 1; + Temp[SLength] = 0; + memcpy (Temp, S, SLength * sizeof (U_64)); + simpleShiftLeftHighPrecision (Temp, TempLength, i); + if (compareHighPrecision (R, RLength, Temp, TempLength) >= 0) + { + subtractHighPrecision (R, RLength, Temp, TempLength); + U += 1 << i; + } + } + + low = compareHighPrecision (R, RLength, mminus, mminus_Length) <= 0; + + memset (Temp + RLength, 0, (STemp_SIZE - RLength) * sizeof (U_64)); + memcpy (Temp, R, RLength * sizeof (U_64)); + TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1; + addHighPrecision (Temp, TempLength, mplus, mplus_Length); + + high = compareHighPrecision (Temp, TempLength, S, SLength) >= 0; + + if (low || high) + break; + + simpleAppendDecimalDigitHighPrecision (R , ++RLength , 0); + simpleAppendDecimalDigitHighPrecision (mplus , ++mplus_Length , 0); + simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0); + while (RLength > 1 && R[RLength - 1] == 0) + --RLength; + while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0) + --mplus_Length; + while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0) + --mminus_Length; + uArray[setCount++] = U; + } + while (1); + + simpleShiftLeftHighPrecision (R, ++RLength, 1); + if (low && !high) + uArray[setCount++] = U; + else if (high && !low) + uArray[setCount++] = U + 1; + else if (compareHighPrecision (R, RLength, S, SLength) < 0) + uArray[setCount++] = U; + else + uArray[setCount++] = U + 1; + + (*env)->ReleaseIntArrayElements (env, uArrayObject, uArray, 0); + + fid = (*env)->GetFieldID (env, clazz, "setCount", "I"); + (*env)->SetIntField (env, inst, fid, setCount); + + fid = (*env)->GetFieldID (env, clazz, "getCount", "I"); + (*env)->SetIntField (env, inst, fid, getCount); + + fid = (*env)->GetFieldID (env, clazz, "firstK", "I"); + (*env)->SetIntField (env, inst, fid, firstK); + +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* NAME, SIGNATURE, FUNCPTR */ + { "bigIntDigitGeneratorInstImpl", "(JIZZI)V" , + Java_org_apache_harmony_luni_util_NumberConverter_bigIntDigitGeneratorInstImpl }, +}; + +int register_org_apache_harmony_luni_util_NumberConvert(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, + "org/apache/harmony/luni/util/NumberConverter", + gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/org_apache_harmony_luni_util_fltparse.c b/luni/src/main/native/org_apache_harmony_luni_util_fltparse.c new file mode 100644 index 0000000..d8a1e68 --- /dev/null +++ b/luni/src/main/native/org_apache_harmony_luni_util_fltparse.c @@ -0,0 +1,609 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "JNIHelp.h" +#include "commonDblParce.h" +#include "cbigint.h" + +#if defined(LINUX) +#define USE_LL +#endif + +#define LOW_I32_FROM_VAR(u64) LOW_I32_FROM_LONG64(u64) +#ifdef HY_LITTLE_ENDIAN +#define LOW_I32_FROM_PTR(ptr64) (*(I_32 *) (ptr64)) +#else +#define LOW_I32_FROM_PTR(ptr64) (*(((I_32 *) (ptr64)) + 1)) +#endif +#define HIGH_I32_FROM_VAR(u64) HIGH_I32_FROM_LONG64(u64) +#define HIGH_I32_FROM_PTR(u64ptr) HIGH_I32_FROM_LONG64_PTR(u64ptr) + +#define MAX_ACCURACY_WIDTH 8 + +#define DEFAULT_WIDTH MAX_ACCURACY_WIDTH + +JNIEXPORT jfloat JNICALL +Java_org_apache_harmony_luni_util_FloatingPointParser_parseFltImpl (JNIEnv * env, + jclass clazz, + jstring s, jint e); +JNIEXPORT jdouble JNICALL +Java_org_apache_harmony_luni_util_FloatingPointParser_parseDblImpl (JNIEnv * env, + jclass clazz, + jstring s, jint e); + +jfloat createFloat1 (JNIEnv * env, U_64 * f, IDATA length, jint e); +jfloat floatAlgorithm (JNIEnv * env, U_64 * f, IDATA length, jint e, + jfloat z); +jfloat createFloat (JNIEnv * env, const char *s, jint e); + +static const U_32 tens[] = { + 0x3f800000, + 0x41200000, + 0x42c80000, + 0x447a0000, + 0x461c4000, + 0x47c35000, + 0x49742400, + 0x4b189680, + 0x4cbebc20, + 0x4e6e6b28, + 0x501502f9 /* 10 ^ 10 in float */ +}; + +#define tenToTheE(e) (*((jfloat *) (tens + (e)))) +#define LOG5_OF_TWO_TO_THE_N 11 + +#define sizeOfTenToTheE(e) (((e) / 19) + 1) + +#define INFINITE_INTBITS (0x7F800000) +#define MINIMUM_INTBITS (1) + +#define MANTISSA_MASK (0x007FFFFF) +#define EXPONENT_MASK (0x7F800000) +#define NORMAL_MASK (0x00800000) +#define FLOAT_TO_INTBITS(flt) (*((U_32 *)(&flt))) + +/* Keep a count of the number of times we decrement and increment to + * approximate the double, and attempt to detect the case where we + * could potentially toggle back and forth between decrementing and + * incrementing. It is possible for us to be stuck in the loop when + * incrementing by one or decrementing by one may exceed or stay below + * the value that we are looking for. In this case, just break out of + * the loop if we toggle between incrementing and decrementing for more + * than twice. + */ +#define INCREMENT_FLOAT(_x, _decCount, _incCount) \ + { \ + ++FLOAT_TO_INTBITS(_x); \ + _incCount++; \ + if( (_incCount > 2) && (_decCount > 2) ) { \ + if( _decCount > _incCount ) { \ + FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \ + } else if( _incCount > _decCount ) { \ + FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \ + } \ + break; \ + } \ + } +#define DECREMENT_FLOAT(_x, _decCount, _incCount) \ + { \ + --FLOAT_TO_INTBITS(_x); \ + _decCount++; \ + if( (_incCount > 2) && (_decCount > 2) ) { \ + if( _decCount > _incCount ) { \ + FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \ + } else if( _incCount > _decCount ) { \ + FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \ + } \ + break; \ + } \ + } +#define ERROR_OCCURED(x) (HIGH_I32_FROM_VAR(x) < 0) + +#define allocateU64(x, n) if (!((x) = (U_64*) malloc((n) * sizeof(U_64)))) goto OutOfMemory; +#define release(r) if ((r)) free((r)); + +jfloat +createFloat (JNIEnv * env, const char *s, jint e) +{ + /* assumes s is a null terminated string with at least one + * character in it */ + U_64 def[DEFAULT_WIDTH]; + U_64 defBackup[DEFAULT_WIDTH]; + U_64 *f, *fNoOverflow, *g, *tempBackup; + U_32 overflow; + jfloat result; + IDATA index = 1; + int unprocessedDigits = 0; + + f = def; + fNoOverflow = defBackup; + *f = 0; + tempBackup = g = 0; + do + { + if (*s >= '0' && *s <= '9') + { + /* Make a back up of f before appending, so that we can + * back out of it if there is no more room, i.e. index > + * MAX_ACCURACY_WIDTH. + */ + memcpy (fNoOverflow, f, sizeof (U_64) * index); + overflow = + simpleAppendDecimalDigitHighPrecision (f, index, *s - '0'); + if (overflow) + { + + f[index++] = overflow; + /* There is an overflow, but there is no more room + * to store the result. We really only need the top 52 + * bits anyway, so we must back out of the overflow, + * and ignore the rest of the string. + */ + if (index >= MAX_ACCURACY_WIDTH) + { + index--; + memcpy (f, fNoOverflow, sizeof (U_64) * index); + break; + } + if (tempBackup) + { + fNoOverflow = tempBackup; + } + } + } + else + index = -1; + } + while (index > 0 && *(++s) != '\0'); + + /* We've broken out of the parse loop either because we've reached + * the end of the string or we've overflowed the maximum accuracy + * limit of a double. If we still have unprocessed digits in the + * given string, then there are three possible results: + * 1. (unprocessed digits + e) == 0, in which case we simply + * convert the existing bits that are already parsed + * 2. (unprocessed digits + e) < 0, in which case we simply + * convert the existing bits that are already parsed along + * with the given e + * 3. (unprocessed digits + e) > 0 indicates that the value is + * simply too big to be stored as a double, so return Infinity + */ + if ((unprocessedDigits = strlen (s)) > 0) + { + e += unprocessedDigits; + if (index > -1) + { + if (e <= 0) + { + result = createFloat1 (env, f, index, e); + } + else + { + FLOAT_TO_INTBITS (result) = INFINITE_INTBITS; + } + } + else + { + result = *(jfloat *) & index; + } + } + else + { + if (index > -1) + { + result = createFloat1 (env, f, index, e); + } + else + { + result = *(jfloat *) & index; + } + } + + return result; + +} + +jfloat +createFloat1 (JNIEnv * env, U_64 * f, IDATA length, jint e) +{ + IDATA numBits; + jdouble dresult; + jfloat result; + + numBits = highestSetBitHighPrecision (f, length) + 1; + numBits -= lowestSetBitHighPrecision (f, length); + if (numBits < 25 && e >= 0 && e < LOG5_OF_TWO_TO_THE_N) + { + return ((jfloat) LOW_I32_FROM_PTR (f)) * tenToTheE (e); + } + else if (numBits < 25 && e < 0 && (-e) < LOG5_OF_TWO_TO_THE_N) + { + return ((jfloat) LOW_I32_FROM_PTR (f)) / tenToTheE (-e); + } + else if (e >= 0 && e < 39) + { + result = (jfloat) (toDoubleHighPrecision (f, length) * pow (10.0, e)); + } + else if (e >= 39) + { + /* Convert the partial result to make sure that the + * non-exponential part is not zero. This check fixes the case + * where the user enters 0.0e309! */ + result = (jfloat) toDoubleHighPrecision (f, length); + + if (result == 0.0) + + FLOAT_TO_INTBITS (result) = MINIMUM_INTBITS; + else + FLOAT_TO_INTBITS (result) = INFINITE_INTBITS; + } + else if (e > -309) + { + int dexp; + U_32 fmant, fovfl; + U_64 dmant; + dresult = toDoubleHighPrecision (f, length) / pow (10.0, -e); + if (IS_DENORMAL_DBL (dresult)) + { + FLOAT_TO_INTBITS (result) = 0; + return result; + } + dexp = doubleExponent (dresult) + 51; + dmant = doubleMantissa (dresult); + /* Is it too small to be represented by a single-precision + * float? */ + if (dexp <= -155) + { + FLOAT_TO_INTBITS (result) = 0; + return result; + } + /* Is it a denormalized single-precision float? */ + if ((dexp <= -127) && (dexp > -155)) + { + /* Only interested in 24 msb bits of the 53-bit double mantissa */ + fmant = (U_32) (dmant >> 29); + fovfl = ((U_32) (dmant & 0x1FFFFFFF)) << 3; + while ((dexp < -127) && ((fmant | fovfl) != 0)) + { + if ((fmant & 1) != 0) + { + fovfl |= 0x80000000; + } + fovfl >>= 1; + fmant >>= 1; + dexp++; + } + if ((fovfl & 0x80000000) != 0) + { + if ((fovfl & 0x7FFFFFFC) != 0) + { + fmant++; + } + else if ((fmant & 1) != 0) + { + fmant++; + } + } + else if ((fovfl & 0x40000000) != 0) + { + if ((fovfl & 0x3FFFFFFC) != 0) + { + fmant++; + } + } + FLOAT_TO_INTBITS (result) = fmant; + } + else + { + result = (jfloat) dresult; + } + } + + /* Don't go straight to zero as the fact that x*0 = 0 independent + * of x might cause the algorithm to produce an incorrect result. + * Instead try the min value first and let it fall to zero if need + * be. + */ + if (e <= -309 || FLOAT_TO_INTBITS (result) == 0) + FLOAT_TO_INTBITS (result) = MINIMUM_INTBITS; + + return floatAlgorithm (env, f, length, e, (jfloat) result); +} + +#if defined(WIN32) +/* disable global optimizations on the microsoft compiler for the + * floatAlgorithm function otherwise it won't properly compile */ +#pragma optimize("g",off) +#endif + +/* The algorithm for the function floatAlgorithm() below can be found + * in: + * + * "How to Read Floating-Point Numbers Accurately", William D. + * Clinger, Proceedings of the ACM SIGPLAN '90 Conference on + * Programming Language Design and Implementation, June 20-22, + * 1990, pp. 92-101. + * + * There is a possibility that the function will end up in an endless + * loop if the given approximating floating-point number (a very small + * floating-point whose value is very close to zero) straddles between + * two approximating integer values. We modified the algorithm slightly + * to detect the case where it oscillates back and forth between + * incrementing and decrementing the floating-point approximation. It + * is currently set such that if the oscillation occurs more than twice + * then return the original approximation. + */ +jfloat +floatAlgorithm (JNIEnv * env, U_64 * f, IDATA length, jint e, jfloat z) +{ + U_64 m; + IDATA k, comparison, comparison2; + U_64 *x, *y, *D, *D2; + IDATA xLength, yLength, DLength, D2Length; + IDATA decApproxCount, incApproxCount; + + x = y = D = D2 = 0; + xLength = yLength = DLength = D2Length = 0; + decApproxCount = incApproxCount = 0; + + do + { + m = floatMantissa (z); + k = floatExponent (z); + + if (x && x != f) + free(x); + + release (y); + release (D); + release (D2); + + if (e >= 0 && k >= 0) + { + xLength = sizeOfTenToTheE (e) + length; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + timesTenToTheEHighPrecision (x, xLength, e); + + yLength = (k >> 6) + 2; + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + simpleShiftLeftHighPrecision (y, yLength, k); + } + else if (e >= 0) + { + xLength = sizeOfTenToTheE (e) + length + ((-k) >> 6) + 1; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + timesTenToTheEHighPrecision (x, xLength, e); + simpleShiftLeftHighPrecision (x, xLength, -k); + + yLength = 1; + allocateU64 (y, 1); + *y = m; + } + else if (k >= 0) + { + xLength = length; + x = f; + + yLength = sizeOfTenToTheE (-e) + 2 + (k >> 6); + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + timesTenToTheEHighPrecision (y, yLength, -e); + simpleShiftLeftHighPrecision (y, yLength, k); + } + else + { + xLength = length + ((-k) >> 6) + 1; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + simpleShiftLeftHighPrecision (x, xLength, -k); + + yLength = sizeOfTenToTheE (-e) + 1; + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + timesTenToTheEHighPrecision (y, yLength, -e); + } + + comparison = compareHighPrecision (x, xLength, y, yLength); + if (comparison > 0) + { /* x > y */ + DLength = xLength; + allocateU64 (D, DLength); + memcpy (D, x, DLength * sizeof (U_64)); + subtractHighPrecision (D, DLength, y, yLength); + } + else if (comparison) + { /* y > x */ + DLength = yLength; + allocateU64 (D, DLength); + memcpy (D, y, DLength * sizeof (U_64)); + subtractHighPrecision (D, DLength, x, xLength); + } + else + { /* y == x */ + DLength = 1; + allocateU64 (D, 1); + *D = 0; + } + + D2Length = DLength + 1; + allocateU64 (D2, D2Length); + m <<= 1; + multiplyHighPrecision (D, DLength, &m, 1, D2, D2Length); + m >>= 1; + + comparison2 = compareHighPrecision (D2, D2Length, y, yLength); + if (comparison2 < 0) + { + if (comparison < 0 && m == NORMAL_MASK) + { + simpleShiftLeftHighPrecision (D2, D2Length, 1); + if (compareHighPrecision (D2, D2Length, y, yLength) > 0) + { + DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + } + else + { + break; + } + } + else + { + break; + } + } + else if (comparison2 == 0) + { + if ((m & 1) == 0) + { + if (comparison < 0 && m == NORMAL_MASK) + { + DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + } + else + { + break; + } + } + else if (comparison < 0) + { + DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + break; + } + else + { + INCREMENT_FLOAT (z, decApproxCount, incApproxCount); + break; + } + } + else if (comparison < 0) + { + DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + } + else + { + if (FLOAT_TO_INTBITS (z) == EXPONENT_MASK) + break; + INCREMENT_FLOAT (z, decApproxCount, incApproxCount); + } + } + while (1); + + if (x && x != f) + free(x); + release (y); + release (D); + release (D2); + return z; + +OutOfMemory: + if (x && x != f) + free(x); + release (y); + release (D); + release (D2); + + FLOAT_TO_INTBITS (z) = -2; + + return z; +} + +#if defined(WIN32) +#pragma optimize("",on) /*restore optimizations */ +#endif + +JNIEXPORT jfloat JNICALL +Java_org_apache_harmony_luni_util_FloatingPointParser_parseFltImpl (JNIEnv * env, + jclass clazz, + jstring s, jint e) +{ + jfloat flt; + const char *str = (*env)->GetStringUTFChars (env, s, 0); + flt = createFloat (env, str, e); + (*env)->ReleaseStringUTFChars (env, s, str); + + if (((I_32) FLOAT_TO_INTBITS (flt)) >= 0) + { + return flt; + } + else if (((I_32) FLOAT_TO_INTBITS (flt)) == (I_32) - 1) + { /* NumberFormatException */ + jniThrowException(env, "java/lang/NumberFormatException", ""); + } + else + { /* OutOfMemoryError */ + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + } + + return 0.0; +} + +JNIEXPORT jdouble JNICALL +Java_org_apache_harmony_luni_util_FloatingPointParser_parseDblImpl (JNIEnv * env, + jclass clazz, + jstring s, jint e) +{ + jdouble dbl; + const char *str = (*env)->GetStringUTFChars (env, s, 0); + dbl = createDouble (env, str, e); + (*env)->ReleaseStringUTFChars (env, s, str); + + if (!ERROR_OCCURED (dbl)) + { + return dbl; + } + else if (LOW_I32_FROM_VAR (dbl) == (I_32) - 1) + { /* NumberFormatException */ + jniThrowException(env, "java/lang/NumberFormatException", ""); + } + else + { /* OutOfMemoryError */ + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + } + + return 0.0; +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* NAME, SIGNATURE, FUNCPTR */ + { "parseFltImpl", "(Ljava/lang/String;I)F", + Java_org_apache_harmony_luni_util_FloatingPointParser_parseFltImpl }, + { "parseDblImpl", "(Ljava/lang/String;I)D", + Java_org_apache_harmony_luni_util_FloatingPointParser_parseDblImpl }, +}; +int register_org_apache_harmony_luni_util_fltparse(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, + "org/apache/harmony/luni/util/FloatingPointParser", + gMethods, NELEM(gMethods)); +} + diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk new file mode 100644 index 0000000..d277f82 --- /dev/null +++ b/luni/src/main/native/sub.mk @@ -0,0 +1,34 @@ +# This file is included by the top-level libcore Android.mk. +# It's not a normal makefile, so we don't include CLEAR_VARS +# or BUILD_*_LIBRARY. + +LOCAL_SRC_FILES := \ + java_io_File.c \ + java_io_FileDescriptor.c \ + java_io_ObjectInputStream.c \ + java_io_ObjectOutputStream.c \ + java_io_ObjectStreamClass.c \ + java_lang_Double.c \ + java_lang_Float.c \ + java_lang_Math.c \ + java_lang_StrictMath.c \ + java_net_InetAddress.cpp \ + java_net_NetworkInterface.c \ + cbigint.c \ + commonDblParce.c \ + org_apache_harmony_luni_util_fltparse.c \ + org_apache_harmony_luni_util_NumberConvert.c \ + org_apache_harmony_luni_platform_OSNetworkSystem.cpp \ + org_apache_harmony_luni_platform_OSFileSystem.cpp \ + org_apache_harmony_luni_platform_OSMemory.cpp + +LOCAL_C_INCLUDES += + +# Any shared/static libs that are listed here must also +# be listed in libs/nativehelper/Android.mk. +# TODO: fix this requirement + +LOCAL_SHARED_LIBRARIES += libutils + +LOCAL_STATIC_LIBRARIES += \ + libfdlibm |