1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
//! # Purpose
//!
//! This module contains the functions exported for use by other languages.
//!
//! If you're writing bindings, you're in the right place. Don't use imageflow_core::ffi, that's
//! *not* correct - those are unstable and will disappear as C is replaced with Rust.
//!
//! Don't call functions against the same context from multiple threads. You can create contexts
//! from as many threads as you like, but you are responsible for synchronizing API calls
//! on a per-context basis if you want to use one context from multiple threads. No use
//! case for multithreaded Context access has been presented, so it  is out of scope for API design.
//!
//!
//!
//!
//! # Memory Lifetimes
//!
//! In order to prevent dangling pointers, we must be correct about memory lifetimes.
//!
//! ## ... when allocated by Imageflow, assume the lifetime of the `context`
//!
//! **In Imageflow, by default, all things created with a context will be destroyed when the
//! context is destroyed.** Don't try to access ANYTHING imageflow has provided after the context is gone.
//!
//! This is very nice, as it means that a client's failure to clean up
//! will have limited impact on the process as a whole - as long as the client at minimum
//! calls `flow_context_destroy` at the end of all possible code paths.
//!
//! However, waiting to free memory and run destructors until the context is destroyed is not ideal;
//! it increases our peak memory usage/needs and may cause operations
//! to fail that would otherwise succeed.
//!
//! There are two ways to mitigate this.
//!
//! 1. When creating an I/O object, request it be cleaned up when the first job it is assigned to is cleaned up (instead of the context).
//! 2. Manually invoke the corresponding destroy function when you're done with the thing.
//!
//! ### Destroying things
//!
//! * An `imageflow_context` should ALWAYS be destroyed with `imageflow_context_destroy`
//! * JsonResponse structures should be released with `imageflow_json_response_destroy`
//! * An `imageflow_job` can be destroyed early with `imageflow_job_destroy`
//!
//! ## ... when allocated by the client, Imageflow only borrows it for the `invocation`
//!
//! **Imageflow assumes that, at minimum, all pointers that you provide to it will, at minimum,
//! remain valid for the duration of the API call.** We'll call this 'borrowing'. Imageflow is
//! just borrowing it for a bit; not taking ownership of the thing.
//!
//! This may seem obvious, but it is not, in fact, guaranteed by garbage-collected languages. They
//! are often oblivious to pointers, and cannot track what data is and is not referenced.
//! Therefore, we suggest that you ensure every allocation made (and handed to Imageflow) is
//! referenced *after* the imageflow API call, preferably in a way that will not be optimized away
//! at runtime. Many languages and FFI libraries offer a utility method just for this purpose.
//!
//! ## ... although Imageflow may borrow some strings for the life of the context, yet not own them.
//!
//! This happens for strings that are usually static constants, and unlikely to be allocated on
//! the heap anyway.
//!
//! * When an Imageflow API asks for a filename, function name, or error message, it will
//!   assume that those strings are pointers that (a) Imageflow is not
//!   responsible for freeing, and (b) will (at least) outlive the `context`.
//!
//! ## ... and it should be very clear when Imageflow is taking ownership of something you created!
//!
//! When Imageflow needs continued access to data that is NOT highly likely to be static, it
//! will be documented.
//!
//! * If you give Imageflow a buffer to read an image from, it will need to access that buffer
//!   much longer than the initial io_create call.
//!
//! ## What if I need something to outlive the `context`?
//!
//! Copy it before the context is destroyed.
//!
//!
//!
//! # Data types
//!
//! Reference for those creating bindings in other languages
//!
//! Two types are platform-specific - use the corresponding pointer or size type that varies with
//! your platform.
//!
//! * libc::c_void (or anything *mut or *const): Platform-sized pointer. 32 or 64 bits.
//! * The above includes *mut Context, *mut Job, *mut JobIo, etc.
//! * libc::size_t (or usize): Unsigned integer, platform-sized. 32 or 64 bits.
//!
//!
//! Treat *mut Context, *mut Job, *mut JobIo, *mut JsonResponse ALL as opaque pointers.
//!
//! ## Strings
//!
//! ASCII is a safe subset of UTF-8; therefore wherever Imageflow asks for UTF-8 encoded bytes, you may provide ASCII instead.
//!
//! You will provide Imageflow with strings in one of 3 ways:
//! * UTF-8 null-terminated. You'll see something like `libc::char`, but no length parameter. Short and likely static strings are usually transmitted this way.
//! * Operating system null-terminated. Only applicable to `imageflow_io_create_for_file`.
//! * UTF-8 buffer with length. You'll usually see *const u8 and a length parameter. This is common for buffers of UTF-8 encoded json.
//!
//! filename: *const libc::c_char
//! function_name: *const libc::c_char
//!
//! Fixed size
//!
//! * u8 (1 unsigned byte)
//! * bool (C99 style, 1 byte, value 0 or 1)
//! * The rest seem self-explanatory.
//! * `i` prefixes signed ints
//! * `u` prefixes unsigned ints.
//! * `f` prefixes floating point
//!
//! Structs
//!
//! Consider all structures to be opaque. Do not attempt to access fields by offsets; rather,
//! use the accessor functions provided.
//!
//!
//! ## Failure behavior
//!
//! If you provide a null pointer for `imageflow_context`, then the process *should* terminate.
//! This "fail fast" behavior offers the best opportunity for a useful stacktrace, and it's not a
//! recoverable error.
//!
//!
//!
#![crate_type = "cdylib"]
#![feature(alloc_system)]
extern crate imageflow_core as c;
extern crate alloc_system;
extern crate libc;
use c::ffi;

pub use c::{Context, Job};
pub use c::ffi::ImageflowJobIo as JobIo;
pub use c::ffi::ImageflowJsonResponse as JsonResponse;
use std::ptr;
use std::io::Write;
use std::ffi::CStr;

#[cfg(test)]
use std::str;

///
/// What is possible with the IO object
#[repr(C)]
pub enum IoMode {
    None = 0,
    ReadSequential = 1,
    WriteSequential = 2,
    ReadSeekable = 5, // 1 | 4,
    WriteSeekable = 6, // 2 | 4,
    ReadWriteSeekable = 15, // 1 | 2 | 4 | 8
}

///
/// Input or output?
#[repr(C)]
#[derive(Copy,Clone)]
pub enum Direction {
    Out = 8,
    In = 4,
}

///
/// When a resource should be closed/freed/cleaned up
///
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum CleanupWith{
    /// When the context is destroyed
    Context = 0,
    /// When the first job that the item is associated with is destroyed. (Not yet implemented)
    FirstJob = 1
}

///
/// How long the provided pointer/buffer will remain valid.
/// Callers must prevent the memory from being freed or moved until this contract expires.
///
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Lifetime{
    /// Pointer will outlive function call. If the host language has a garbage collector, call the appropriate method to ensure the object pointed to will not be collected or moved until the call returns. You may think host languages do this automatically in their FFI system. Most do not.
    OutlivesFunctionCall = 0,
    /// Pointer will outlive context. If the host language has a GC, ensure that you are using a data type guaranteed to neither be moved or collected automatically.
    OutlivesContext = 1,
}



/// Creates a static, null-terminated Rust string, and
/// returns a ` *const libc::c_char` pointer to it.
///
/// Useful for API invocations that require a static C string
#[macro_export]
macro_rules! static_char {
    ($lit:expr) => {
        concat!($lit, "\0").as_ptr() as *const libc::c_char
    }
}
unsafe fn uw(outer: *mut Context) -> *mut c::ffi::ImageflowContext{
    if outer.is_null(){
        ptr::null_mut()
    }else {
        (&mut *outer).flow_c()
    }
}



/// Creates and returns an imageflow context.
/// An imageflow context is required for all other imageflow API calls.
///
/// An imageflow context tracks
/// * error state
/// * error messages
/// * stack traces for errors (in C land, at least)
/// * context-managed memory allocations
/// * performance profiling information
///
/// **Contexts are not thread-safe!** Once you create a context, *you* are responsible for ensuring that it is never involved in two overlapping API calls.
///
/// Returns a null pointer if allocation fails.
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_create() -> *mut Context {
    Context::abi_create_boxed().map(|b| Box::into_raw(b)).unwrap_or(std::ptr::null_mut())
}

/// Begins the process of destroying the context, yet leaves error information intact
/// so that any errors in the tear-down process can be
/// debugged with imageflow_context_error_and_stacktrace.
///
/// Returns true if no errors occurred. Returns false if there were tear-down issues.
///
/// *Behavior is undefined if context is a null or invalid ptr.*
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_begin_terminate(context: *mut Context) -> bool {
    if context.is_null() {
        //Is this the best solution? Failing early is best for detecting bugs... Or we could just let null access do its thing.
        //but it may wait until farther up the call stack to actually try to dereference data... :(
        let _ = std::io::stderr().write_all(b"A null pointer was provided to imageflow_context_begin_terminate. Terminating process.\n");
        std::process::exit(70);
    }else{
        (&mut *context).abi_begin_terminate()
    }
}

/// Destroys the imageflow context and frees the context object.
/// Only use this with contexts created using imageflow_context_create
///
/// Behavior is undefined if context is a null or invalid ptr; may segfault on free(NULL);
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_destroy(context: *mut Context) {
    let _ = Box::from_raw(context);

}


#[test]
fn test_create_destroy() {
    exercise_create_destroy();
}

pub fn exercise_create_destroy() {
    unsafe {
        let c = imageflow_context_create();
        assert!(!c.is_null());
        assert!(imageflow_context_begin_terminate(c));
        imageflow_context_destroy(c);
    }
}

/// Returns true if the context is in an error state. You must immediately deal with the error,
/// as subsequent API calls will fail or cause undefined behavior until the error state is cleared
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_has_error(context: *mut Context) -> bool {
    (&*context).error().abi_has_error()
}

/// Clear the error state. This assumes that you know which API call failed and the problem has
/// been resolved. Don't use this unless you're sure you've accounted for all possible
/// inconsistent state (and fully understand the code paths that led to the error).
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_clear_error(context: *mut Context) {
    (&mut *context).error_mut().abi_clear_error()
}

/// Prints the error messages and stacktrace to the given buffer in UTF-8 form; writes a null
/// character to terminate the string, and *ALSO* returns the number of bytes written.
///
///
/// Happy(ish) path: Returns the length of the error message written to the buffer.
/// Sad path: Returns -1 if buffer_length was too small or buffer was nullptr.
/// full_file_path, if true, will display the directory associated with the files in each stack frame.
///
/// Please be accurate with the buffer length, or a buffer overflow will occur.
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_error_and_stacktrace(context: *mut Context,
                                                                buffer: *mut libc::c_char,
                                                                buffer_length: libc::size_t,
                                                                full_file_path: bool)
                                                                -> i64 {
    ffi::flow_context_error_and_stacktrace(uw(context), buffer as *mut u8, buffer_length, full_file_path)
}

/// Returns the numeric code associated with the error.
///
/// ## Error codes
///
/// * 0 - No error condition.
/// * 10 - Out Of Memory condition (malloc/calloc/realloc failed).
/// * 20 - I/O error
/// * 30 - Invalid internal state (assertion failed; you found a bug).
/// * 40 - Error: Not implemented. (Feature not implemented).
/// * 50 - Invalid argument provided
/// * 51 - Null argument provided
/// * 52 - Invalid dimensions
/// * 53 - Unsupported pixel format
/// * 54 - Item does not exist
/// * 60 - Image decoding failed
/// * 61 - Image encoding failed
/// * 70 - Graph invalid
/// * 71 - Graph is cyclic
/// * 72 - Invalid inputs to node
/// * 73 - Maximum graph passes exceeded
/// * 1024 - Other error; something else happened
/// * 1025 through 2147483647 are reserved for user-defined errors
///
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_error_code(context: *mut Context) -> i32 {
    (&mut *context).error_mut().abi_error_code()
}

/// Prints the error to stderr and exits the process if an error has been raised on the context.
/// If no error is present, the function returns false.
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
///
/// THIS PRINTS DIRECTLY TO STDERR! Do not use in any kind of service! Command-line usage only!
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_print_and_exit_if_error(context: *mut Context) -> bool {
    (&mut *context).error_mut().abi_abort_and_print_on_error()
}

///
/// Raises an error on the context.
///
/// Returns `true` on success, `false`  if an error was already present.
///
/// Designed to be safe(ish) for use in out-of-memory scenarios; no additional allocations are made.
///
/// See `imageflow_context_error_code` for a list of error codes.
///
///
/// # Expectations
///
/// * Strings `message` and `function_name`, and `filename` should be null-terminated UTF-8 strings.
/// * The lifetime of `message` is expected to exceed the duration of this function call.
/// * The lifetime of `filename` and `function_name` (if provided), is expected to match or exceed the lifetime of `context`.
/// * You may provide a null value for `filename` or `function_name`, but for the love of puppies,
/// don't provide a dangling or invalid pointer, that will segfault... a long time later.
///
/// # Caveats
///
/// * You cannot raise a second error until the first has been cleared with
///  `imageflow_context_clear_error`. You'll be ignored, as will future
///   `imageflow_add_to_callstack` invocations.
/// * Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
/// * Behavior is undefined if `message` is an invalid ptr; immediate segfault likely.
/// * If you provide an error code of zero (why?!), a different error code will be provided.
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_raise_error(context: *mut Context,
                                                       error_code: i32,
                                                       message: *const libc::c_char,
                                                       filename: *const libc::c_char,
                                                       line: i32,
                                                       function_name: *const libc::c_char)
                                                       -> bool {

    let message_cstr = if message.is_null() { None } else { Some(CStr::from_ptr(message))};
    let filename_cstr = if filename.is_null() { None } else { Some(CStr::from_ptr(filename))};
    let function_name_cstr = if function_name.is_null() { None } else { Some(CStr::from_ptr(function_name))};
    let line_optional = if line >= 0 { Some(line)} else { None };

    (&mut *context).error_mut().abi_raise_error_c_style(error_code, message_cstr, filename_cstr, line_optional, function_name_cstr)

}

///
/// Adds the given filename, line number, and function name to the call stack.
/// Strings `function_name`, and `filename` should be null-terminated UTF-8 strings who will outlive `context`
///
/// Returns `true` if add was successful.
///
/// # Will fail and return false if...
///
/// * You haven't previously called `imageflow_context_raise_error`
/// * You tried to raise a second error without clearing the first one. Call will be ignored.
/// * You've exceeded the capacity of the call stack (which, at one point, was 14). But this
///   category of failure is acceptable.
///
///
/// # Expectations
///
/// * An error has been raised.
/// * You may provide a null value for `filename` or `function_name`, but for the love of puppies,
/// don't provide a dangling or invalid pointer, that will segfault... a long time later.
/// * The lifetime of `file` and `function_name` (if provided), is expected to match
///   or exceed the lifetime of `context`.
/// * All strings must be null-terminated, C-style, valid UTF-8.
///
/// # Caveats
///
/// * Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_add_to_callstack(context: *mut Context,
                                                            filename: *const libc::c_char,
                                                            line: i32,
                                                            function_name: *const libc::c_char)
                                                            -> bool {

    let filename_cstr = if filename.is_null() { None } else { Some(CStr::from_ptr(filename))};
    let function_name_cstr = if function_name.is_null() { None } else { Some(CStr::from_ptr(function_name))};
    let line_optional = if line >= 0 { Some(line)} else { None };

    (&mut *context).error_mut().abi_add_to_callstack_c_style(filename_cstr, line_optional, function_name_cstr)
}


#[test]
pub fn test_error_handling() {
    exercise_error_handling();
}
pub fn exercise_error_handling() {
    unsafe {
        let c = imageflow_context_create();
        assert!(!c.is_null());

        assert!(!imageflow_context_has_error(c));
        assert_eq!(imageflow_context_error_code(c), 0);

        // While the strings are static, the CStrings are not.
        // But they will persist until the end of the block, which is all we need
        let message = static_char!("Test message");
        let filename = static_char!(file!());
        let function_name = static_char!("test_error_handling");

        // Let's raise a nice error
        assert!(imageflow_context_raise_error(c, 1025, message, filename, 335, function_name));

        // Check it registered
        assert!(imageflow_context_has_error(c));
        assert_eq!(imageflow_context_error_code(c), 1025);

        // Add a backtrace
        assert!(imageflow_context_add_to_callstack(c, filename, 342, ptr::null()));
        assert!(imageflow_context_add_to_callstack(c, filename, 20, ptr::null()));
        assert!(imageflow_context_add_to_callstack(c, ptr::null(), 0, ptr::null()));

        // Let's see how it handles a insufficient buffer
        let mut tiny_buf: [i8; 30] = [0; 30];
        assert_eq!(-1,
        imageflow_context_error_and_stacktrace(c, &mut tiny_buf[0], 30, true));


        // And check the output looks right
        let mut buf: [u8; 2048] = [0; 2048];
        let buf_used = imageflow_context_error_and_stacktrace(c, std::mem::transmute(&mut buf[0]), 2048, true);

        assert!(buf_used >= 0);
        let actual_string = ::std::str::from_utf8(&buf[0..buf_used as usize] ).unwrap();


        // let expected_string = "User defined error : Test message\nsrc/lib.rs:335: in function test_error_handling\nsrc/lib.rs:342: in function (unknown)\nsrc/lib.rs:20: in function (unknown)\n(unknown):0: in function (unknown)\n";
        assert!(actual_string.starts_with("User defined error : Test message\nsrc"));

        // raising a second error should fail
        assert!(!imageflow_context_raise_error(c,
                                               1025,
                                               message,
                                               filename,
                                               line!() as i32,
                                               ptr::null()));
        // as should trying to add to the call stack
        assert!(!imageflow_context_add_to_callstack(c, filename, line!() as i32, ptr::null()));
        assert!(!imageflow_context_add_to_callstack(c, filename, line!() as i32, ptr::null()));

        // Clearing the error should work
        imageflow_context_clear_error(c);
        assert!(!imageflow_context_has_error(c));
        assert_eq!(imageflow_context_error_code(c), 0);


        // And it should be possible to report another error
        assert!(imageflow_context_raise_error(c, 1025, message, ptr::null(), 1, ptr::null()));
        imageflow_context_clear_error(c);

        // And cleanup should go smoothly
        assert!(imageflow_context_begin_terminate(c));
        imageflow_context_destroy(c);
    }
}




///
/// Writes fields from the given imageflow_json_response to the locations referenced.
/// The buffer pointer sent out will be a UTF-8 byte array of the given length (not null-terminated). It will
/// also become invalid if the JsonResponse associated is freed, or if the context is destroyed.
///
#[no_mangle]
pub unsafe extern fn imageflow_json_response_read(context: *mut Context,
                                                  response_in: *const JsonResponse,
                                                  status_code_out: *mut i64,
                                                  buffer_utf8_no_nulls_out: *mut *const u8,
                                                  buffer_size_out: *mut libc::size_t) -> bool {
    if context.is_null() {
        return false;
    }
    if response_in.is_null() {
        imageflow_context_raise_error(context,
                                      51,
                                      static_char!("response_in is null"),
                                      static_char!(file!()),
                                      line!() as i32,
                                      ptr::null());
        return false;
    }

    if !status_code_out.is_null() {
        *status_code_out = (*response_in).status_code;
    }
    if !buffer_utf8_no_nulls_out.is_null() {
        *buffer_utf8_no_nulls_out = (*response_in).buffer_utf8_no_nulls;
    }
    if !buffer_size_out.is_null() {
        *buffer_size_out = (*response_in).buffer_size;
    }
    return true;
}


/// Frees memory associated with the given object (and owned objects) after
/// running any owned or attached destructors. Returns false if something went wrong during tear-down.
///
/// Returns true if the object to destroy is a null pointer, or if tear-down was successful.
///
/// Behavior is undefined if the pointer is dangling or not a valid memory reference.
/// Although certain implementations catch
/// some kinds of invalid pointers, a segfault is likely in future revisions).
///
/// Behavior is undefined if the context provided does not match the context with which the
/// object was created.
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_json_response_destroy(context: *mut Context,
                                                         response: *mut JsonResponse)
                                                         -> bool {
    ffi::flow_destroy(uw(context), response as *mut libc::c_void, ptr::null(), 0)
}

///
/// Sends a JSON message to the imageflow_context
///
/// The context is provided `method`, which determines which code path will be used to
/// process the provided JSON data and compose a response.
///
/// * `method` and `json_buffer` are only borrowed for the duration of the function call. You are
///    responsible for their cleanup (if necessary - static strings are handy for things like
///    `method`).
/// * `method` should be a UTF-8 null-terminated string.
///   `json_buffer` should be a UTF-8 encoded buffer (not null terminated) of length json_buffer_size.
///
/// The function will return NULL if a JSON response could not be allocated (or if some other
/// bug occurred). If a null pointer is returned, consult the standard error methods of `context`
/// for more detail.
///
/// The response can be cleaned up with `imageflow_json_response_destroy`
///
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn imageflow_context_send_json(context: *mut Context,
                                                     method: *const libc::c_char,
                                                     json_buffer: *const u8,
                                                     json_buffer_size: libc::size_t)
                                                     -> *const JsonResponse {
    imageflow_send_json(context, ptr::null_mut(), ptr::null_mut(), method, json_buffer, json_buffer_size)
}

///
/// Sends a JSON message to the imageflow_job
///
/// The recipient is provided `method`, which determines which code path will be used to
/// process the provided JSON data and compose a response.
///
/// * `method` and `json_buffer` are only borrowed for the duration of the function call. You are
///    responsible for their cleanup (if necessary - static strings are handy for things like
///    `method`).
/// * `method` should be a UTF-8 null-terminated string.
///   `json_buffer` should be a UTF-8 encoded buffer (not null terminated) of length json_buffer_size.
///
/// The function will return NULL if a JSON response could not be allocated (or if some other
/// bug occurred). If a null pointer is returned, consult the standard error methods of `context`
/// for more detail.
///
/// The response can be cleaned up with `imageflow_json_response_destroy`
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn imageflow_job_send_json(context: *mut Context,
                                                 job: *mut Job,
                                                 method: *const libc::c_char,
                                                 json_buffer: *const libc::uint8_t,
                                                 json_buffer_size: libc::size_t)
                                                 -> *const JsonResponse {
    imageflow_send_json(context, job, ptr::null_mut(), method, json_buffer, json_buffer_size)
}

///
/// Sends a JSON message to one of 3 recipients.
///
/// 1. `imageflow_context`, If both `job` and `io` are both null. Required.
/// 2. `imageflow_job`, if only `io` is null.
/// 3. `imageflow_io`, if `io` is not null. `job` is ignored.
///
/// The recipient is then provided `method`, which determines which code path will be used to
/// process the provided JSON data and compose a response.
///
/// * `method` and `json_buffer` are only borrowed for the duration of the function call. You are
///    responsible for their cleanup (if necessary - static strings are handy for things like
///    `method`).
///
/// The function will return NULL if a JSON response could not be allocated (or if some other
/// bug occurred). If a null pointer is returned, consult the standard error methods of `context`
/// for more detail.
///
/// The response can be cleaned up with `imageflow_json_response_destroy`
///
/// Behavior is undefined if `context` is a null or invalid ptr; segfault likely.
#[allow(unused_variables)]
unsafe fn imageflow_send_json(context: *mut Context,
                              job: *mut Job,
                              io: *mut JobIo,
                              method: *const i8,
                              json_buffer: *const libc::uint8_t,
                              json_buffer_size: libc::size_t)
                              -> *const JsonResponse {
    //TODO: check for nulls in json_BUFFER AND METHOD

    // It doesn't appear that this allocates anything
    // But I'm curious how that Rust knows not to deallocate the string itself
    // Oh. It's borrowed. CStr is borrowed from the pointer. Pointers don't have lifetimes. &str is borrowed from CStr
    let method_str = ::std::ffi::CStr::from_ptr(method as *const i8).to_str().unwrap(); //TODO, throw exception if not UTF-8, argument error

    let json_bytes = std::slice::from_raw_parts(json_buffer, json_buffer_size);

    // TODO: possibly iterate access to force segfaults earlier?


    let mut ctx = &mut *context;


    let response;

    if job == ptr::null_mut() && io == ptr::null_mut() {
        response = ctx.message(method_str, json_bytes).unwrap(); //Unwrap for invalid context
    } else if io == ptr::null_mut() {
        //TODO: perform lookup instead of dereferencing!
        response =(&mut *job).message(method_str, json_bytes).unwrap();
    } else{
        panic!("Messaging JobIo not implemented");
    }


    let ptr = ctx.create_abi_json_response(response.response_json, response.status_code);
    //TODO: if ptr == null,
    //Handle alloc failure
    ptr
}



#[test]
fn test_message() {
    exercise_json_message();
}

pub fn exercise_json_message() {
    unsafe {
        let c = imageflow_context_create();
        assert!(!c.is_null());

        let method_in = static_char!("brew_coffee");
        let json_in = "{}";
        let expected_response = c::JsonResponse::teapot();
        let expected_json_out = ::std::str::from_utf8(
            expected_response.response_json.as_ref()).unwrap();
        let expected_reponse_status = expected_response.status_code;

        let response = imageflow_send_json(c,
                                           ptr::null_mut(),
                                           ptr::null_mut(),
                                           method_in,
                                           json_in.as_ptr(),
                                           json_in.len());

        assert!(response != ptr::null());

        let mut json_out_ptr: *const u8 = ptr::null_mut();
        let mut json_out_size: usize = 0;
        let mut json_status_code: i64 = 0;

        assert!(imageflow_json_response_read(c,
                                             response,
                                             &mut json_status_code,
                                             &mut json_out_ptr,
                                             &mut json_out_size));


        let json_out_str =
        ::std::str::from_utf8(std::slice::from_raw_parts(json_out_ptr, json_out_size)).unwrap();
        assert_eq!(json_out_str, expected_json_out);

        assert_eq!(json_status_code, expected_reponse_status);

        imageflow_context_destroy(c);
    }
}

///
/// Creates an imageflow_io object to wrap a filename.
///
/// The filename should be a null-terminated string. It should be written in codepage used by your operating system for handling `fopen` calls.
/// https://msdn.microsoft.com/en-us/library/yeby3zcb.aspx
///
/// If the filename is fopen compatible, you're probably OK.
///
/// As always, `mode` is not enforced except for the file open flags.
///
#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn imageflow_io_create_for_file(context: *mut Context,
                                                      mode: IoMode,
                                                      filename: *const libc::c_char,
                                                      cleanup: CleanupWith)
                                                      -> *mut JobIo {
    // TODO: validate that 'owner' is capable of being an owner

    ffi::flow_io_create_for_file(uw(context), std::mem::transmute(mode), filename, uw(context) as *const libc::c_void)
}


///
/// Creates an imageflow_io structure for reading from the provided buffer.
/// You are ALWAYS responsible for freeing the memory provided in accordance with the Lifetime value.
/// If you specify OutlivesFunctionCall, then the buffer will be copied.
///
///
#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn imageflow_io_create_from_buffer(context: *mut Context,
                                                         buffer: *const u8,
                                                         buffer_byte_count: libc::size_t,
                                                            lifetime: Lifetime,
                                                            cleanup: CleanupWith)
                                                         -> *mut JobIo {

    let mut final_buffer = buffer;
    if lifetime == Lifetime::OutlivesFunctionCall {
        let buf : *mut u8 = c::ffi::flow_context_calloc(uw(context), 1, buffer_byte_count, ptr::null(), uw(context) as *const libc::c_void, ptr::null(), 0) as *mut u8 ;
        if buf.is_null() {
            //TODO: raise OOM
            return ptr::null_mut();
        }
        ptr::copy_nonoverlapping(buffer, buf, buffer_byte_count);

        final_buffer = buf;
    }
    ffi::flow_io_create_from_memory(uw(context), std::mem::transmute(IoMode::ReadSeekable), final_buffer, buffer_byte_count, uw(context) as *mut libc::c_void, ptr::null())
}


///
/// Creates an imageflow_io structure for writing to an expanding memory buffer.
///
/// Reads/seeks, are, in theory, supported, but unless you've written, there will be nothing to read.
///
/// The I/O structure and buffer will be freed with the context.
///
///
/// Returns null if allocation failed; check the context for error details.
#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn imageflow_io_create_for_output_buffer(context: *mut Context)
                                                               -> *mut JobIo {
    // The current implementation of output buffer only sheds its actual buffer with the context.
    // No need for the shell to have an earlier lifetime for mem reasons.
    ffi::flow_io_create_for_output_buffer(uw(context), uw(context) as *mut libc::c_void)
}


// Returns false if the flow_io struct is disposed or not an output buffer type (or for any other error)
//

///
/// Provides access to the underlying buffer for the given imageflow_io object.
///
/// Ensure your length variable always holds 64-bits.
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_io_get_output_buffer(context: *mut Context,
                                                        io: *mut JobIo,
                                                        result_buffer: *mut *const u8,
                                                        result_buffer_length: *mut libc::size_t)
                                                        -> bool {

    let mut result_len: usize = 0;
    let b = ffi::flow_io_get_output_buffer(uw(context), io, result_buffer, &mut result_len);
        (* result_buffer_length) = result_len;
    b
}

///
/// Provides access to the underlying buffer for the given imageflow_io object.
///
/// Ensure your length variable always holds 64-bits
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_job_get_output_buffer_by_id(context: *mut Context,
                                                               job: *mut Job,
                                                               io_id: i32,
                                                               result_buffer: *mut *const u8,
                                                               result_buffer_length: *mut libc::size_t)
                                                               -> bool {
    (&*job).get_io(io_id).map(|io|
        imageflow_io_get_output_buffer(context, io, result_buffer, result_buffer_length)
    ).unwrap_or_else(|e| { e.write_to_context_ptr(context); false })
}


///
/// Creates an imageflow_job, which permits the association of imageflow_io instances with
/// numeric identifiers and provides a 'sub-context' for job execution
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_job_create(context: *mut Context) -> *mut Job {
    &mut *(&*context).create_job()
}


///
/// Looks up the imageflow_io pointer from the provided io_id
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_job_get_io(context: *mut Context,
                                              job: *mut Job,
                                              io_id: i32)
                                              -> *mut JobIo {
    (&*job).get_io(io_id).unwrap_or(ptr::null_mut())
}

///
/// Associates the imageflow_io object with the job and the assigned io_id.
///
/// The io_id will correspond with io_id in the graph
///
/// direction is in or out.
#[no_mangle]
pub unsafe extern "C" fn imageflow_job_add_io(context: *mut Context,
                                              job: *mut Job,
                                              io: *mut JobIo,
                                              io_id: i32,
                                              direction: Direction)
                                              -> bool {
    (&mut *job).add_io(io, io_id, std::mem::transmute(direction))
        .map(|_| true)
        .unwrap_or_else(|e| { e.write_to_context_ptr(context); false })

}

///
/// Destroys the provided imageflow_job
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_job_destroy(context: *mut Context, job: *mut Job) -> bool {
    (&*context).abi_try_remove_job(job)
}


///
/// Allocates zeroed memory that will be freed with the context.
///
/// * filename/line may be used for debugging purposes. They are optional. Provide null/-1 to skip.
/// * `filename` should be an null-terminated UTF-8 or ASCII string which will outlive the context.
///
/// Returns null(0) on failure.
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_memory_allocate(context: *mut Context,
                                                    bytes: libc::size_t,
                                                    filename: *const libc::c_char,
                                                    line: i32) -> *mut libc::c_void {

    ffi::flow_context_calloc(uw(context), 1, bytes, ptr::null(), uw(context) as *const libc::c_void, filename, line)
}

///
/// Frees memory allocated with imageflow_context_memory_allocate early.
///
/// * filename/line may be used for debugging purposes. They are optional. Provide null/-1 to skip.
/// * `filename` should be an null-terminated UTF-8 or ASCII string which will outlive the context.
///
/// Returns false on failure.
///
#[no_mangle]
pub unsafe extern "C" fn imageflow_context_memory_free(context: *mut Context,
                                                       pointer: *mut libc::c_void,
                                                       filename: *const libc::c_char,
                                                       line: i32) -> bool {
    ffi::flow_destroy(uw(context), pointer, filename, line)
}

#[test]
fn test_allocate_free() {
    unsafe{
        let c = imageflow_context_create();
        let bytes = 100;
        let ptr = imageflow_context_memory_allocate(c, bytes, static_char!(file!()),
                                                    line!() as i32) as *mut u8;
        assert!(ptr != ptr::null_mut());

        for x in 0..bytes{
            assert_eq!(*ptr.offset(x as isize), 0);
        }
        assert!(imageflow_context_memory_free(c, ptr as *mut libc::c_void, static_char!(file!()),
                                              line!() as i32));

        imageflow_context_destroy(c);
        //imageflow_context_destroy(c);
    }
}