URL url = new URL(urlSpec);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
InputStream in = connection.getInputStream();
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
I am especially curious about this part
while(bytesRead = in.read(buffer))
We know that asigements are treated as statements in kotlin while in java they are treated as expressions, so this construct is only possible in java.
What is best way to translate this java code into kotlin?
Instead of translating the code literally, make use of Kotlin's stdlib which offers a number of useful extension functions. Here's one version
val text = URL(urlSpec).openConnection().inputStream.bufferedReader().use { it.readText() }
To answer the original question: You're right, assignments are not treated as expressions. Therefore you will need to separate the assignment and the comparison. Take a look at the implementation in the stdlib for an example:
public fun Reader.copyTo(out: Writer, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
var charsCopied: Long = 0
val buffer = CharArray(bufferSize)
var chars = read(buffer)
while (chars >= 0) {
out.write(buffer, 0, chars)
charsCopied += chars
chars = read(buffer)
}
return charsCopied
}
Source: https://github.com/JetBrains/kotlin/blob/a66fc9043437d2e75f04feadcfc63c61b04bd196/libraries/stdlib/src/kotlin/io/ReadWrite.kt#L114
You could use apply block to execute the assignment:
val input= connection.getInputStream();
var bytesRead = 0;
val buffer = ByteArray(1024)
while (input.read(buffer).apply { bytesRead = this } > 0) {
out.write(buffer, 0, bytesRead);
}
You could use something like this
This operation may be little heavy as a function is created each iteration.
val url = URL("urlSpec")
val connection = url.openConnection() as HttpURLConnection
val `in` = connection.inputStream
val buffer = ByteArray(1024)
var bytesRead: Int? = null
while ({ bytesRead = `in`.read(buffer); bytesRead }() != null) {
out.write(buffer, 0, bytesRead!!)
}
out.close()
Related
class SoundPlayer(context: Context) {
// For sound FX
private val soundPool: SoundPool = SoundPool(10, // Here
AudioManager.STREAM_MUSIC,
0)
companion object {
var playerExplodeID = -1
var invaderExplodeID = -1
var shootID = -1
var damageShelterID = -1
var uhID = -1
var ohID = -1
}
init {
try {
// Create objects of the 2 required classes
val assetManager = context.assets
var descriptor: AssetFileDescriptor
// Load our fx in memory ready for use
descriptor = assetManager.openFd("shoot.ogg")
shootID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("invaderexplode.ogg")
invaderExplodeID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("damageshelter.ogg")
damageShelterID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("playerexplode.ogg")
playerExplodeID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("damageshelter.ogg")
damageShelterID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("uh.ogg")
uhID = soundPool.load(descriptor, 0)
descriptor = assetManager.openFd("oh.ogg")
ohID = soundPool.load(descriptor, 0)
} catch (e: IOException) {
// Print an error message to the console
Log.e("error", "failed to load sound files")
}
}
fun playSound(id: Int){
soundPool.play(id, 1f, 1f, 0, 0, 1f)
}
}
i have a problem with SoundPool cant use it is says constructor SoundPool is deprecated
i'm kinda new so don't know how to fix this (watched many videos and searched everywhere but i cant fix it)
so maybe someone can help me out tell me what to do
When something is deprecated, there always should be a hint what to use instead.
So you need to use SoundPool.Builder to create new instance of an object.
But there is one issue if you target API level that was release before SoundPool.Builder then you will get ClassNotFoundException.
So general approach is to check the API level and do things in old way before API X(when new feature was introduced), and a new way after API X:
#Suppress("DEPRECATION")
fun buildSoundPool(maxStreams: Int):SoundPool =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val attrs = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
SoundPool.Builder()
.setAudioAttributes(attrs)
.setMaxStreams(maxStreams)
.build()
} else {
SoundPool(maxStreams, AudioManager.STREAM_MUSIC, 0)
}
Then:
private val soundPool: SoundPool = buildSoundPool(10)
Also I do recommend to use my custom implementation of SoundPool because lots of platform dependent issues that was introduced in original SoundPool on different versions on Android.
I converted some Java classes to kotlin and an "Assignments are not expressions, and only expressions are allowed in this context" error pops up when I try to run this code which worked fine in Java:
#Throws(IOException::class)
private fun readAll(rd: Reader): String {
val sb = StringBuilder()
var cp: Int
while ((cp = rd.read()) != -1) {
sb.append(cp.toChar())
}
return sb.toString()
}
The line causing the problem:
while ((cp = rd.read()) != -1)
Exactly as the message says in Kotlin you can't use the assignment as an expression.
You can do this:
private fun readAll(rd: Reader): String {
val sb = StringBuilder()
var cp: Int
do {
cp = rd.read()
if (cp == -1)
break
sb.append(cp.toChar())
} while (true) // your choice here to stop the loop
return sb.toString()
}
In Kotlin you can't do this:
while ((cp = rd.read()) != -1)
You should use something like this:
var cp = rd.read()
while (cp != -1) {
// your logic here
cp = rd.read()
}
Or something like this:
while (true) {
val cp = rd.read()
if (cp < 0) break
// your logic here
}
Because assignment (cp = rd.read()) is expression in Java, but not in Kotlin.
On certain images, when I call:
PdfImageObject pimg = new PdfImageObject(stream);
Image bmp = pimg.GetDrawingImage();
The Image that is returned is twisted. I've seen this before and it usually has to do with byte alignment but I'm not sure how to get around this.
The /DecodeParms for this object are /EndOfLine true /K 0 /Columns 3300.
I have tried using the GetStreamBytesRaw() with BitMiracle.LibTiff and with it I can get the data formatted properly although the image is rotated. I'd prefer for GetDrawingImage() to decode the data properly if possible, assuming that is the problem.
I could provide the PDF via email if requested.
Thanks,
Darren
For anyone else that runs across this scenario here is my solution. The key to this puzzle was understanding that /K 0 is G3, /K -1 (or anything less than 0) is G4 /K 1 (or anything greater than 0) is G3-2D.
The twisting happens when you try to make G3 compressed data fit into a G4 image which it appears that is what iTextSharp may be doing. I know it definitely does not work with how I have iTextSharp implemented in my project. I confess that I cannot decipher all the decoding stuff that iTextSharp is doing so it could be something I'm missing too.
EndOfLine didn't have any part in this puzzle but I still think putting line feeds in binary data is a strange practice.
99% of this code came from BitMiracle.LibTiff.Net - Thank you.
int nK = 0;// Default to 0 like the PDF Spec
PdfObject oDecodeParms = stream.Get(PdfName.DECODEPARMS);
if (oDecodeParms is PdfDictionary)
{
PdfObject oK0 = ((PdfDictionary)oDecodeParms).Get(PdfName.K);
if (oK0 != null)
nK = ((PdfNumber)oK0).IntValue;
}
using (MemoryStream ms = new MemoryStream())
{
using (Tiff tiff = Tiff.ClientOpen("custom", "w", ms, new TiffStream()))
{
tiff.SetField(TiffTag.IMAGEWIDTH, width);
tiff.SetField(TiffTag.IMAGELENGTH, height);
if (nK == 0 || nK > 0) // 0 = Group 3, > 0 = Group 3 2D
tiff.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX3);
else if (nK < 0) // < 0 = Group 4
tiff.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX4);
tiff.SetField(TiffTag.BITSPERSAMPLE, bpc);
tiff.SetField(TiffTag.SAMPLESPERPIXEL, 1);
tiff.WriteRawStrip(0, rawBytes, rawBytes.Length); //saving the tiff file using the raw bytes retrieved from the PDF.
tiff.Close();
}
TiffStreamForBytes byteStream = new TiffStreamForBytes(ms.ToArray());
using (Tiff input = Tiff.ClientOpen("bytes", "r", null, byteStream))
{
int stride = input.ScanlineSize();
Bitmap result = new Bitmap(width, height, pixelFormat);
ColorPalette palette = result.Palette;
palette.Entries[0] = System.Drawing.Color.White;
palette.Entries[1] = System.Drawing.Color.Black;
result.Palette = palette;
for (int i = 0; i < height; i++)
{
Rectangle imgRect = new Rectangle(0, i, width, 1);
BitmapData imgData = result.LockBits(imgRect, ImageLockMode.WriteOnly, pixelFormat);
byte[] buffer = new byte[stride];
input.ReadScanline(buffer, i);
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, imgData.Scan0, buffer.Length);
result.UnlockBits(imgData);
}
}
}
/// <summary>
/// Custom read-only stream for byte buffer that can be used
/// with Tiff.ClientOpen method.
/// </summary>
public class TiffStreamForBytes : TiffStream
{
private byte[] m_bytes;
private int m_position;
public TiffStreamForBytes(byte[] bytes)
{
m_bytes = bytes;
m_position = 0;
}
public override int Read(object clientData, byte[] buffer, int offset, int count)
{
if ((m_position + count) > m_bytes.Length)
return -1;
Buffer.BlockCopy(m_bytes, m_position, buffer, offset, count);
m_position += count;
return count;
}
public override void Write(object clientData, byte[] buffer, int offset, int count)
{
throw new InvalidOperationException("This stream is read-only");
}
public override long Seek(object clientData, long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Begin:
if (offset > m_bytes.Length)
return -1;
m_position = (int)offset;
return m_position;
case SeekOrigin.Current:
if ((offset + m_position) > m_bytes.Length)
return -1;
m_position += (int)offset;
return m_position;
case SeekOrigin.End:
if ((m_bytes.Length - offset) < 0)
return -1;
m_position = (int)(m_bytes.Length - offset);
return m_position;
}
return -1;
}
public override void Close(object clientData)
{
// nothing to do
return;
}
public override long Size(object clientData)
{
return m_bytes.Length;
}
}
I've found the following code in my boss's project:
Dim strBuilder As New System.Text.StringBuilder("", 1000000)
Before I call him out on it, I'd like to confirm whether this line actually sets a megabyte (or two megabytes in Unicode?) of memory aside for that one stringbuilder?
That initializes a Char() of length 1000000.
So the actual size needed in memory is 2000000 Bytes = ~2 MB since a char is unicode and needs 2 bytes.
Edit: Just in case your boss doesn't believe, this is reflected with ILSpy:
// System.Text.StringBuilder
[SecuritySafeCritical]
public unsafe StringBuilder(string value, int startIndex, int length, int capacity)
{
if (capacity < 0)
{
throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", new object[]
{
"capacity"
}));
}
if (length < 0)
{
throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum", new object[]
{
"length"
}));
}
if (startIndex < 0)
{
throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
}
if (value == null)
{
value = string.Empty;
}
if (startIndex > value.Length - length)
{
throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_IndexLength"));
}
this.m_MaxCapacity = 2147483647;
if (capacity == 0)
{
capacity = 16;
}
if (capacity < length)
{
capacity = length;
}
this.m_ChunkChars = new char[capacity];
this.m_ChunkLength = length;
fixed (char* ptr = value)
{
StringBuilder.ThreadSafeCopy(ptr + (IntPtr)startIndex, this.m_ChunkChars, 0, length);
}
}
You could try calling GC.GetTotalMemory() before and after that allocation, and see if it increases. Note: this is not a good, scientific way to do this, but may prove your point.
I have been asked to provide a WCF service that allows a blob (potentially 1GB) to be downloaded in chunks as an offset byte[] for consumption by a Silverlight application. Essentially, the operation will have a parameter for number of bytes to offset and the max number of bytes to return, nothing complex I think.
The code I have so far is:
[OperationContract]
public byte[] Download(String url, int blobOffset, int bufferSize)
{
var blob = new CloudBlob(url);
using(var blobStream = blob.OpenRead())
{
var buffer = new byte[bufferSize];
blobStream.Seek(blobOffset, SeekOrigin.Begin);
int numBytesRead = blobStream.Read(buffer, 0, bufferSize);
if (numBytesRead != bufferSize)
{
var trimmedBuffer = new byte[numBytesRead];
Array.Copy(buffer, trimmedBuffer, numBytesRead);
return trimmedBuffer;
}
return buffer;
}
}
I have tested this (albeit with relatively small files < 2MB) and it does work, but my questions are:
Can someone suggest improvements to the code?
Is there a better approach given the requirement?
using (BlobStream blobStream = blob.OpenRead())
{
bool getSuccess = false;
int getTries = 0;
rawBytes = new byte[blobStream.Length];
blobStream.Seek(0, SeekOrigin.Begin);
int blockSize = 4194304; //Start at 4 mb per batch
int index = 0;
int documentSize = rawBytes.Length;
while (getTries <= 10 && !getSuccess)
{
try
{
int batchSize = blockSize;
while (index < documentSize)
{
if ((index + batchSize) > documentSize)
batchSize = documentSize - index;
blobStream.Read(rawBytes, index, batchSize);
index += batchSize;
}
getSuccess = true;
}
catch (Exception e)
{
if (getTries > 9)
throw e;
else
blockSize = blockSize / 2; // Reduce by half for each attempt
}
finally
{ getTries++; }
}
}
You could return the blob as a stream instead of a byte array. There is a code sample in a related question here: Returning Azure BLOB from WCF service as a Stream - Do we need to close it?
Note there are some restrictions on which bindings you can use when you return a stream.